Express JS中res.download完成后执行操作的更佳方案?
在Express中下载完成后安全删除临时CSV文件
我有一个Express GET路由用于下载CSV文件,功能正常,但临时目录里的文件会不断累积。直接在res.download()后调用fs.unlink()会导致下载失败,因为文件在发送完成前就被删除了;用固定时长的setTimeout()作为临时方案,但网络差时会失效。需要在下载完成后再执行文件删除的可靠方法。
解决方案
Express的response对象会在响应完全发送完毕后触发finish事件,你可以监听这个事件来执行文件删除操作。同时监听close事件可以处理客户端中途断开下载的情况,避免文件残留。
回调版本代码示例
const fs = require('fs'); const express = require('express'); const router = express.Router(); router.get('/downloadCsv', function(req, res, next) { const downloadCsvPath = '/path/to/your/temp/file.csv'; res.download(downloadCsvPath); // 定义删除文件的函数 const deleteFile = () => { fs.unlink(downloadCsvPath, (err) => { if (err) { // 如果文件已经被删除(比如close事件和finish事件都触发),忽略ENOENT错误 if (err.code !== 'ENOENT') { console.error('删除临时文件失败:', err); } return; } console.log('临时文件已删除:', downloadCsvPath); }); }; // 响应发送完成时执行删除 res.on('finish', deleteFile); // 客户端断开连接时执行删除 res.on('close', deleteFile); });
Promise/Async-Await版本(更简洁)
如果你使用Node.js 10+,可以用fs.promises来避免回调嵌套:
const fs = require('fs').promises; const express = require('express'); const router = express.Router(); router.get('/downloadCsv', async function(req, res, next) { const downloadCsvPath = '/path/to/your/temp/file.csv'; res.download(downloadCsvPath); const deleteFile = async () => { try { await fs.unlink(downloadCsvPath); console.log('临时文件已删除:', downloadCsvPath); } catch (err) { if (err.code !== 'ENOENT') { console.error('删除临时文件失败:', err); } } }; res.on('finish', deleteFile); res.on('close', deleteFile); });
原理说明
finish事件:在Express将所有响应数据发送到客户端,响应正式结束后触发,这时候文件已经不再被读取,可以安全删除。close事件:当客户端提前断开连接(比如用户中途取消下载)时触发,这时候也需要删除临时文件,避免垃圾文件累积。- 为什么不能直接在
res.download()后调用fs.unlink():res.download()是异步操作,调用后Express才开始读取文件并发送给客户端,此时文件处于被占用状态,提前删除会导致读取失败,出现ENOENT错误。 - 为什么
setTimeout()不可靠:下载时间受网络状况影响,固定时长的超时无法适配所有场景,容易出现文件还没下载完就被删除,或者文件早就下载完了还没删除的情况。
内容的提问来源于stack exchange,提问作者Reid
相关产品推荐
相关产品推荐

