import type { Readable } from 'node:stream' import { Router } from 'express' import { getStoredFileObject, isPublicObjectKey, } from '../services/file-storage/file-storage-service.js' import { buildNotFoundPayload, sendRouteError } from '../utils/http.js' const router = Router() router.get('/object', async (req, res) => { let readerToDestroy: Readable | null = null try { const rawKey = String(req.query.key || '').trim() const isPublic = isPublicObjectKey(rawKey) const object = await getStoredFileObject(req.query.key, req.query.variant) readerToDestroy = object.reader if (object.etag) { res.setHeader('ETag', object.etag) if (req.headers['if-none-match'] === object.etag) { object.reader.destroy() readerToDestroy = null res.status(304).end() return } } if (object.lastModified) { res.setHeader('Last-Modified', object.lastModified.toUTCString()) if (req.headers['if-modified-since']) { const ifModifiedSince = new Date(req.headers['if-modified-since']) if (!isNaN(ifModifiedSince.getTime()) && object.lastModified <= ifModifiedSince) { object.reader.destroy() readerToDestroy = null res.status(304).end() return } } } if (object.size > 0) { res.setHeader('Content-Length', object.size) } res.setHeader('Content-Type', object.contentType || 'application/octet-stream') res.setHeader('X-Content-Type-Options', 'nosniff') if (isPublic) { res.setHeader('Cache-Control', 'public, max-age=2592000, stale-while-revalidate=604800') } else { res.setHeader('Cache-Control', 'private, max-age=300') } res.on('close', () => { if (!res.writableEnded) { object.reader.destroy() } }) object.reader.on('error', (error) => { object.reader.destroy() if (!res.headersSent) { sendRouteError(res, error, '读取文件失败', '[files/object]') } else { res.destroy(error) } }) object.reader.pipe(res) readerToDestroy = null } catch (error) { if (readerToDestroy) { readerToDestroy.destroy() } sendRouteError(res, error, '读取文件失败', '[files/object]') } }) router.use((req, res) => { res.status(404).json(buildNotFoundPayload(req)) }) export default router