Nest.js FileInterceptor如何等待文件移动完成后返回访问URL
问题根因
出现该问题的核心是错误使用了multer的diskStorage配置API:
diskStorage的filename、destination是错误优先的同步回调接口,本身不支持async/Promise写法- 现有代码给
filename加了async修饰,还错误使用await callback(),直接打乱了multer内部的执行时序:FileInterceptor没有等到文件完整写入静态资源目录、写入流关闭,就提前执行了控制器逻辑,导致接口返回URL时文件还在写入过程中,此时发起请求就会返回503错误 - 正常情况下,
FileInterceptor会在文件完全落盘后才会调用控制器方法,不需要额外在控制器中编写轮询等待逻辑
修复方案
首先修正filename回调的写法,Node.js原生crypto.randomUUID()是同步方法,不需要加await,同时去掉回调函数的async修饰,让multer按原生同步时序执行:
import { randomUUID } from 'crypto'; import { extname, join } from 'path'; import { Post, UseInterceptors, UploadedFile } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { diskStorage } from 'multer'; // 其他业务依赖省略 @Post('upload') @UseInterceptors( FileInterceptor('file', { storage: diskStorage({ destination: join(__dirname, '../../../', 'static/files'), // 保持普通函数写法,不要加async filename: (req, file, callback) => { const fileExtName = extname(file.originalname); // randomUUID为同步方法,直接获取返回值即可,不需要await const randomName = randomUUID(); // 直接执行回调,不要加await callback(null, `${randomName}${fileExtName}`); }, }), }), ) async uploadFile( @UploadedFile() file: Express.Multer.File, ): Promise<DataResponseDto<UploadedFileDto>> { // 执行到此处时,FileInterceptor已保证文件完整写入磁盘 const url = `${this.configService.apiConfig.apiStaticUrl}/${file.filename}`; return { data: { uploadedFileUrl: url } }; }
可选兜底校验
如果需要做极端场景的兼容,可以在控制器中加一个轻量的同步文件存在校验,确认文件可访问后再返回结果:
import { existsSync } from 'fs'; // ... async uploadFile( @UploadedFile() file: Express.Multer.File, ): Promise<DataResponseDto<UploadedFileDto>> { const staticDir = join(__dirname, '../../../', 'static/files'); const filePath = join(staticDir, file.filename); // 同步校验文件是否存在 if (!existsSync(filePath)) { throw new Error('文件写入异常,请重试'); } const url = `${this.configService.apiConfig.apiStaticUrl}/${file.filename}`; return { data: { uploadedFileUrl: url } }; }
注意:不要使用异步文件访问API、加固定延时或者轮询逻辑,修正回调写法后multer本身的执行时序完全可靠,上述校验仅作为兜底逻辑使用。
如果后续确实需要异步生成文件名(如查库获取命名规则、调用远程服务生成文件名),请先执行完异步逻辑再初始化multer配置,或使用支持Promise的第三方multer存储引擎,不要在原生diskStorage的回调中使用async/await破坏时序。
内容的提问来源于stack exchange,提问作者Martin K
相关产品推荐
相关产品推荐

