NestJS控制器操作完成后文件删除异常问题排查与修复
问题解决:NestJS文件处理后错误场景下文件无法删除的问题
核心原因
res.download异步未等待:原代码中res.download是回调式异步操作,未等待其执行完成就触发删除逻辑,错误场景下可能文件仍在处理/响应传输中就被尝试删除,或因响应未处理完毕导致后续逻辑异常。- 删除逻辑重复且时机混乱:catch块中直接执行删除,但部分错误可能发生在文件下载阶段,此时部分目标文件尚未创建,删除逻辑会抛出不必要的错误;同时未统一处理所有流程分支的资源清理。
解决方案
1. 用finally块统一处理资源清理
无论流程成功还是失败,finally块都会执行,避免重复编写删除逻辑,确保所有场景下的文件都能被清理。
2. 封装res.download为Promise并等待完成
将Express的回调式res.download转为Promise,等待响应完全发送给客户端后再执行删除,防止文件还在被下载就被删除。
3. 完善错误响应逻辑
在catch块中先判断响应是否已发送,再发送错误信息,避免出现Cannot set headers after they are sent的异常。
修改后的代码示例
控制器方法
@Post(':id/export') async potrfolioExport( @Body() files: string[], @Param('id') id: any, @Res() res: Response ) { const paths = files.map((elem) => `./uploads/${elem}.pptx`); const allFilesToDelete = [...paths, './uploads/template.pptx', './uploads/output.pptx']; // 把res.download封装为Promise,等待响应发送完成 const sendDownload = () => new Promise<void>((resolve, reject) => { res.download('./uploads/output.pptx', (err) => { err ? reject(err) : resolve(); }); }); try { await this.filesService.downloadFiles(files); await this.filesService.downloadAdditionalFile('template.pptx'); await this.mergePres(paths); // 等待下载响应发送完成后再继续 await sendDownload(); } catch (error) { console.error("操作错误: ", error.body); // 避免重复发送响应 if (!res.headersSent) { res.status(500).json({ message: error.message, error }); } } finally { // 无论成功失败,最终执行文件删除 try { await this.filesService.deleteFiles(allFilesToDelete); } catch (deleteErr) { console.error("文件删除失败: ", deleteErr.message); // 删除失败不影响响应结果,仅记录日志 } } }
deleteFiles方法优化(可选)
将同步的fs.existsSync改为异步方法,避免阻塞事件循环:
async deleteFiles(filePaths: string[]): Promise<void> { const unlink = fs.promises.unlink; const access = fs.promises.access; for (const filePath of filePaths) { try { // 异步判断文件是否存在 await access(filePath, fs.constants.F_OK); await unlink(filePath); console.log(`${filePath} deleted`); } catch (error) { console.log(`${filePath} 不存在或删除失败: ${error.message}`); } } }
关键说明
finally块确保所有流程分支都会触发文件删除,彻底避免资源泄漏。- 等待
res.download完成后再执行后续逻辑,保证客户端已拿到文件后再删除服务器端的副本。 - 增加
res.headersSent判断,防止因响应已发送导致的二次响应错误。
内容的提问来源于stack exchange,提问作者Илья Никитин
相关产品推荐
相关产品推荐

