NestJS上传gzip文件至GCS后下载文件损坏问题
GCS上传gzip文件损坏问题排查
你的上传逻辑存在4处核心错误,直接导致最终存储的文件损坏:
- 二进制内容被错误转码
你在接口入口调用了req.setEncoding('utf-8'),gzip属于二进制格式文件,强制按utf-8编码解析会直接破坏原始字节结构。后续遍历chunk将每个字符转二进制字符串拼接为strOut的逻辑完全无效,最终得到的字符串和原始gzip文件的字节内容没有任何关系,相当于你把原文件内容彻底改写后再存入GCS。 - 重复执行gzip压缩
你本地读取上传的本身就是已经压缩完成的.gz文件,调用blob.save时传入gzip:true参数会触发SDK自动对传入内容再做一次gzip压缩,同时你还在元数据中配置了contentEncoding: 'gzip',下载时服务端会按单次gzip规则解压,得到的内容必然是损坏的。 - 请求流处理逻辑错误
data事件会在请求传输过程中多次触发,每次仅返回文件的一个分块。你现在在data事件回调中直接执行上传逻辑,每次收到分块就新建Storage实例、覆盖写入目标文件,最终GCS中存储的只有最后一个分块的内容,文件本身就不完整。且每次请求都新建Storage实例属于不必要的资源浪费。 - 接口无响应返回
现有逻辑没有在上传完成/失败后给客户端返回响应,客户端会一直等待直到超时,无法感知真实的上传结果。
修复方案
服务端正确实现
将Storage客户端初始化提前到模块顶层复用,不修改请求的原始二进制编码,完整收集所有请求分块后再统一上传,关闭SDK自动gzip压缩,去掉重复的contentEncoding配置:
// 模块顶层初始化GCS客户端,全局复用 const { Storage } = require('@google-cloud/storage'); const storage = new Storage({ keyFilename: 'datastore.json' }); const targetBucket = storage.bucket('bucket_name'); @Post('/uploadFile') async uploadFile(@Req() req: Request, @Res() res: Response) { const chunks = []; // 收集原始二进制分块,不做任何编码转换 req.on('data', (chunk) => chunks.push(chunk)); req.on('end', async () => { try { const fileBuffer = Buffer.concat(chunks); const targetFile = targetBucket.file(req.headers.filename as string); await targetFile.save(fileBuffer, { metadata: { contentType: req.headers.filetype }, resumable: false, gzip: false // 关闭自动压缩,避免对已压缩的gzip文件重复处理 }); res.status(200).send({ success: true, message: '文件上传完成' }); } catch (err) { res.status(500).send({ success: false, message: '上传失败', error: err.message }); } }); req.on('error', (err) => { res.status(500).send({ success: false, message: '请求流读取失败', error: err.message }); }); }
客户端说明
现有客户端流读取上传的逻辑不需要修改,只要注意不要在请求头中额外添加Content-Encoding: gzip字段即可。
验证方式:上传前先记录本地.gz文件的大小和MD5值,服务端拼接完
fileBuffer后打印Buffer长度,和本地文件大小一致说明字节未被破坏;上传完成后下载文件比对MD5,值一致则说明文件完整无损坏。
内容的提问来源于stack exchange,提问作者Vedant Pruthi
相关产品推荐
相关产品推荐

