Node.js将Flutter上传的octet-stream流转为MP4上传至GCP Cloud Storage
首先明确两个核心认知:
application/octet-stream只是通用二进制流的标识,你客户端上传的本身就是MP4格式的二进制数据,不需要做额外的格式转换。只要上传到GCS时指定正确的content-type元数据,存储后的文件就是可正常播放的MP4文件。- 你之前
req.pipe(blobStream)失效的绝大多数原因,是你所用的Node服务框架(比如Express)全局挂载了express.json()、express.urlencoded()这类请求体解析中间件,这类中间件会提前消费完req流的所有数据,导致后续pipe时流已经为空。
前置配置
如果你用的是Express框架,需要给上传接口单独排除通用的请求体解析中间件,确保req是未被消费的原始流:
// 该路由不要使用全局的express.json()、express.urlencoded()等中间件 app.post('/upload-video', (req, res) => { // 上传逻辑写在这个路由处理函数内 })
方案1:修复pipe逻辑(更推荐)
pipe会自动处理流的背压、数据传输顺序,不需要手动管理分块,是最简洁稳定的实现:
const { Storage } = require('@google-cloud/storage'); const storage = new Storage(); app.post('/upload-video', async (req, res) => { try { const newFileName = 'newFile.mp4'; // 建议添加.mp4后缀,避免后续文件识别异常 const bucket = storage.bucket('dev.appspot.com/postVideos'); const blob = bucket.file(newFileName); const blobStream = blob.createWriteStream({ metadata: { contentType: 'video/mp4' }, resumable: false // 小于100M的文件可以关闭,大文件建议开启可恢复上传 }); // 直接将原始请求流管道到GCS的可写流 req.pipe(blobStream); // 等待上传完成 await new Promise((resolve, reject) => { blobStream.on('error', reject); blobStream.on('finish', resolve); }); res.status(200).send('上传成功'); } catch (err) { console.error('上传失败:', err); res.status(500).send('上传失败'); } });
方案2:手动监听data事件处理分块(适配你当前的写法)
如果你需要自行处理接收的chunk数据,直接将chunk写入GCS的可写流即可,流会自动按顺序拼接内容,所有数据接收完成后调用end方法结束上传:
// 接收分块数据并写入GCS流 req.on('data', (chunk) => { const canWrite = blobStream.write(chunk); // 处理背压:如果GCS流缓冲区已满,暂停接收请求数据,避免内存溢出 if (!canWrite) { req.pause(); } }); // GCS流缓冲区排空后,恢复接收请求数据 blobStream.on('drain', () => { req.resume(); }); // 请求数据接收完成,结束GCS上传流 req.on('end', () => { blobStream.end(); console.log('全部分块接收完成,GCS上传处理中'); });
额外注意事项
- 大文件上传建议开启GCS的可恢复上传能力,只需要在
createWriteStream配置中把resumable设为true,SDK会自动处理分块、断点续传逻辑,不需要你自己拆分管理分块。 - 如果是客户端主动将文件拆分为多个请求上传,你需要给每个分块添加序号标识,服务端先将所有分块存储到临时路径,全部收齐后调用GCS的compose接口合并为一个完整文件即可。
内容的提问来源于stack exchange,提问作者cpboyce
相关产品推荐
相关产品推荐

