Files
SuBlog/server/routes/posts/[slug]/img/[...file].ts
T
sutong b7addb5c7a chore: 初始化项目仓库
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 21:27:39 +08:00

40 lines
1.1 KiB
TypeScript

import { readFile } from 'node:fs/promises'
import { resolve, extname } from 'node:path'
import { existsSync } from 'node:fs'
const MIME: Record<string, string> = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.webp': 'image/webp',
'.avif': 'image/avif',
'.svg': 'image/svg+xml',
}
export default defineEventHandler(async (event) => {
const url = getRequestURL(event)
const pathMatch = url.pathname.match(/^\/posts\/([a-z0-9-]+)\/img\/(.+)$/)
if (!pathMatch) return
const [, slug, filename] = pathMatch
// 防止路径遍历攻击
if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
throw createError({ statusCode: 400 })
}
const ext = extname(filename).toLowerCase()
if (!MIME[ext]) return
const filePath = resolve('content/posts', slug, 'img', filename)
if (!existsSync(filePath)) {
throw createError({ statusCode: 404 })
}
const data = await readFile(filePath)
setResponseHeader(event, 'content-type', MIME[ext])
setResponseHeader(event, 'cache-control', 'public, max-age=31536000')
return data
})