NestJS中如何在WebSocket场景下使用FileInterceptor?
WebSocket场景下文件上传实现说明
NestJS内置的FileInterceptor是基于multer封装的HTTP场景专用拦截器,依赖HTTP请求的multipart/form-data流解析逻辑,无法直接在WebSocket网关中生效——WebSocket传输的是独立的文本/二进制帧,没有内置的multipart格式解析能力,直接挂载你写的那段拦截器代码不会触发文件解析逻辑。
可直接复用原有配置的实现方案
你原配置里的上传目录、文件大小限制规则可以完全平移,根据文件大小选对应实现即可:
方案1:小文件(如头像)Base64传输(实现成本最低)
- 客户端处理:选中文件后读取为Base64字符串,和其他业务参数一起封装成普通WebSocket消息发送,消息结构参考:
{ "event": "uploadProfilePhoto", "data": { "fileName": "avatar.jpg", "fileBase64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..." } } - 服务端处理:在WebSocket消息订阅方法里手动完成校验、存盘逻辑,完全对齐你原来的拦截器配置:
import { WebSocketGateway, SubscribeMessage, MessageBody } from '@nestjs/websockets'; import * as fs from 'fs'; import * as path from 'path'; // 平移你原拦截器的配置 const UPLOAD_DEST = '../uploads/profilePictures'; const MAX_FILE_SIZE = 10 * byte; // 和你原limits.fileSize配置保持一致 @WebSocketGateway({ cors: true }) export class FileUploadGateway { @SubscribeMessage('uploadProfilePhoto') async handleAvatarUpload(@MessageBody() payload: { fileName: string, fileBase64: string }) { // 剥离Base64文件头转Buffer const pureBase64 = payload.fileBase64.replace(/^data:image\/\w+;base64,/, ''); const fileBuffer = Buffer.from(pureBase64, 'base64'); // 对齐原拦截器的大小校验逻辑 if (fileBuffer.length > MAX_FILE_SIZE) { return { success: false, message: '文件大小超出限制' }; } // 目录不存在则递归创建 if (!fs.existsSync(UPLOAD_DEST)) { fs.mkdirSync(UPLOAD_DEST, { recursive: true }); } // 生成唯一文件名写入目标路径 const saveFileName = `${Date.now()}-${path.basename(payload.fileName)}`; const fullSavePath = path.join(UPLOAD_DEST, saveFileName); fs.writeFileSync(fullSavePath, fileBuffer); return { success: true, message: '上传成功', filePath: fullSavePath }; } }
方案2:大文件分片传输(适合大于1MB的文件)
如果要传更大的文件,单帧传Base64会有性能问题,用分片逻辑实现:
- 客户端把文件按100KB~1MB的大小切成分片,逐片发送,消息里携带文件唯一标识、当前分片序号、总分片数、分片二进制内容
- 服务端用内存或者临时文件缓存每个上传任务的分片,收齐所有分片后按序号合并成完整文件,同样做大小校验后存入你配置的
../uploads/profilePictures目录即可,过程中还可以实时给客户端回传上传进度。
注意:不要尝试直接给WebSocket订阅方法添加
@UseInterceptors(FileInterceptor(...))装饰器。FileInterceptor内部依赖HTTP上下文的request对象获取multipart流,WebSocket上下文不存在该对象,运行时会直接抛出空对象错误。
内容的提问来源于stack exchange,提问作者GramosTV
相关产品推荐
相关产品推荐

