Files
guanwang/server/api/media/[...path].get.ts

44 lines
1.6 KiB
TypeScript

import { createReadStream, statSync } from 'node:fs'
import { resolve, sep } from 'node:path'
const contentTypes: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
webp: 'image/webp',
}
export default defineEventHandler((event) => {
const requested = getRouterParam(event, 'path') || ''
const uploadsRoot = resolve(process.cwd(), 'data', 'uploads')
const legacyRoot = resolve(process.cwd(), 'data', 'media')
let mediaRoot = uploadsRoot
let filePath = resolve(mediaRoot, requested)
if (!filePath.startsWith(`${mediaRoot}${sep}`)) {
throw createError({ statusCode: 400, statusMessage: '无效的文件路径' })
}
let fileStat
try {
fileStat = statSync(filePath)
} catch {
const legacyPath = resolve(legacyRoot, requested)
if (!legacyPath.startsWith(`${legacyRoot}${sep}`)) throw createError({ statusCode: 400, statusMessage: '无效的文件路径' })
try { fileStat = statSync(legacyPath); filePath = legacyPath; mediaRoot = legacyRoot }
catch { throw createError({ statusCode: 404, statusMessage: '图片不存在' }) }
}
if (!fileStat.isFile()) {
throw createError({ statusCode: 404, statusMessage: '图片不存在' })
}
const extension = filePath.split('.').pop()?.toLowerCase() || ''
const contentType = contentTypes[extension]
if (!contentType) {
throw createError({ statusCode: 415, statusMessage: '不支持的文件类型' })
}
setHeader(event, 'Content-Type', contentType)
setHeader(event, 'Content-Length', fileStat.size)
setHeader(event, 'Cache-Control', 'public, max-age=31536000, immutable')
return sendStream(event, createReadStream(filePath))
})