Skip to content

Commit

Permalink
Merge pull request #3584 from mikiher/author-image-performance
Browse files Browse the repository at this point in the history
Improve author image performance
  • Loading branch information
advplyr authored Nov 3, 2024
2 parents 654b1d6 + 68fd1d6 commit 978c2b0
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 17 deletions.
2 changes: 1 addition & 1 deletion client/components/covers/AuthorImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default {
},
imgSrc() {
if (!this.imagePath) return null
return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?ts=${this.updatedAt}`
}
},
methods: {
Expand Down
4 changes: 2 additions & 2 deletions server/Auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Auth {
constructor() {
// Map of openId sessions indexed by oauth2 state-variable
this.openIdAuthSession = new Map()
this.ignorePattern = /\/api\/items\/[^/]+\/cover/
this.ignorePatterns = [/\/api\/items\/[^/]+\/cover/, /\/api\/authors\/[^/]+\/image/]
}

/**
Expand All @@ -28,7 +28,7 @@ class Auth {
* @private
*/
authNotNeeded(req) {
return req.method === 'GET' && this.ignorePattern.test(req.originalUrl)
return req.method === 'GET' && this.ignorePatterns.some((pattern) => pattern.test(req.originalUrl))
}

ifAuthNeeded(middleware) {
Expand Down
21 changes: 14 additions & 7 deletions server/controllers/AuthorController.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,16 +381,23 @@ class AuthorController {
*/
async getImage(req, res) {
const {
query: { width, height, format, raw },
author
query: { width, height, format, raw }
} = req

if (!author.imagePath || !(await fs.pathExists(author.imagePath))) {
Logger.warn(`[AuthorController] Author "${author.name}" has invalid imagePath: ${author.imagePath}`)
return res.sendStatus(404)
}
const authorId = req.params.id

if (raw) {
const author = await Database.authorModel.findByPk(authorId)
if (!author) {
Logger.warn(`[AuthorController] Author "${authorId}" not found`)
return res.sendStatus(404)
}

if (!author.imagePath || !(await fs.pathExists(author.imagePath))) {
Logger.warn(`[AuthorController] Author "${author.name}" has invalid imagePath: ${author.imagePath}`)
return res.sendStatus(404)
}

return res.sendFile(author.imagePath)
}

Expand All @@ -399,7 +406,7 @@ class AuthorController {
height: height ? parseInt(height) : null,
width: width ? parseInt(width) : null
}
return CacheManager.handleAuthorCache(res, author, options)
return CacheManager.handleAuthorCache(res, authorId, options)
}

/**
Expand Down
17 changes: 11 additions & 6 deletions server/managers/CacheManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,22 +134,22 @@ class CacheManager {
/**
*
* @param {import('express').Response} res
* @param {import('../models/Author')} author
* @param {String} authorId
* @param {{ format?: string, width?: number, height?: number }} options
* @returns
*/
async handleAuthorCache(res, author, options = {}) {
async handleAuthorCache(res, authorId, options = {}) {
const format = options.format || 'webp'
const width = options.width || 400
const height = options.height || null

res.type(`image/${format}`)

var path = Path.join(this.ImageCachePath, `${author.id}_${width}${height ? `x${height}` : ''}`) + '.' + format
var cachePath = Path.join(this.ImageCachePath, `${authorId}_${width}${height ? `x${height}` : ''}`) + '.' + format

// Cache exists
if (await fs.pathExists(path)) {
const r = fs.createReadStream(path)
if (await fs.pathExists(cachePath)) {
const r = fs.createReadStream(cachePath)
const ps = new stream.PassThrough()
stream.pipeline(r, ps, (err) => {
if (err) {
Expand All @@ -160,7 +160,12 @@ class CacheManager {
return ps.pipe(res)
}

let writtenFile = await resizeImage(author.imagePath, path, width, height)
const author = await Database.authorModel.findByPk(authorId)
if (!author || !author.imagePath || !(await fs.pathExists(author.imagePath))) {
return res.sendStatus(404)
}

let writtenFile = await resizeImage(author.imagePath, cachePath, width, height)
if (!writtenFile) return res.sendStatus(500)

var readStream = fs.createReadStream(writtenFile)
Expand Down
2 changes: 1 addition & 1 deletion server/routers/ApiRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ class ApiRouter {
this.router.patch('/authors/:id', AuthorController.middleware.bind(this), AuthorController.update.bind(this))
this.router.delete('/authors/:id', AuthorController.middleware.bind(this), AuthorController.delete.bind(this))
this.router.post('/authors/:id/match', AuthorController.middleware.bind(this), AuthorController.match.bind(this))
this.router.get('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.getImage.bind(this))
this.router.get('/authors/:id/image', AuthorController.getImage.bind(this))
this.router.post('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.uploadImage.bind(this))
this.router.delete('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.deleteImage.bind(this))

Expand Down

0 comments on commit 978c2b0

Please sign in to comment.