如何在Node.js Express REST API中为图片返回304状态码
解决Express返回图片始终200,首次下载后返回304的问题
你的问题核心在于:图片并非存储在本地文件系统,Express默认的静态文件缓存机制(自动生成ETag、处理304响应)无法自动生效,所以得手动实现HTTP缓存逻辑。下面是具体的解决步骤和代码示例:
1. 为图片生成唯一标识(ETag)
ETag是资源的唯一身份标记,用来判断资源是否发生变化。你可以根据实际情况选择生成方式:
- 如果图片内容永远不会变:直接用图片的唯一ID(比如你示例里的
1234)生成即可; - 如果图片可能更新:建议用文件内容的哈希值,或者结合数据库中该文件的
更新时间戳+唯一ID来生成,确保内容变化时ETag也会变化。
2. 对比缓存验证字段,返回对应状态码
客户端首次请求后,会把ETag存在If-None-Match请求头里。当再次请求时,你需要做两件事:
- 对比请求头的
If-None-Match和你生成的ETag; - 如果两者一致,直接返回
304 Not Modified,不用发送图片内容;如果不一致,返回图片内容并设置缓存相关响应头。
3. 结合你的代码修改示例
下面是适配你现有路由的具体代码:
router.get('/file/:organisation/:table/:filename', async function (req, res, next) { try { // 从数据库获取图片数据(假设返回包含内容、唯一ID、更新时间的对象) const fileData = await db.resolveTable(req.params.organisation, req.params.table, req.params.filename); if (!fileData) { return res.status(404).send('File not found'); } // 生成ETag:这里用文件ID+更新时间戳确保内容变化时ETag更新 const etag = `"${fileData.id}-${fileData.updateTime.getTime()}"`; // 如果图片永远不会变,也可以用固定值:const etag = `"${req.params.filename}"`; // 检查客户端缓存的ETag是否匹配 if (req.headers['if-none-match'] === etag) { return res.status(304).send(); } // 设置缓存相关响应头 res.set({ 'ETag': etag, 'Cache-Control': 'public, max-age=31536000' // 缓存1年,可根据需求调整时长 }); // 返回图片内容,自动识别MIME类型 res.type(req.params.filename.split('.').pop()); res.send(fileData.content); // 假设fileData.content是图片的Buffer或二进制数据 } catch (err) { next(err); } });
额外补充
Cache-Control头可以控制客户端和中间缓存(比如CDN)的策略:public表示允许中间缓存,max-age是缓存的秒数;- 除了ETag方案,你也可以用
Last-Modified+If-Modified-Since的组合:设置Last-Modified为文件最后更新时间,然后检查请求头的If-Modified-Since是否匹配,匹配则返回304,原理和ETag一致。
内容的提问来源于stack exchange,提问作者minisaurus
相关产品推荐
相关产品推荐

