Node.js中如何向Google Cloud Storage上报上传进度
实现Google Cloud Storage文件上传进度追踪
你原来尝试监听的data事件是Node.js Readable流的事件,而@google-cloud/storage的createWriteStream返回的是Writable流,所以不会触发。该库实际支持上传进度的监听,主要有两种可靠的实现方式,下面结合你的现有代码给出具体方案:
方式1:使用官方提供的onProgress回调(推荐)
在调用createWriteStream时,可以传入配置参数,其中onProgress会在上传过程中被定期调用,回调参数里包含已上传字节数和总字节数,直接用来计算进度即可。
修改后的完整代码:
router.post("/upload-file", multer.single("file"), async (req, res, next) => { try{ const file = req.file; if (!file) { return res.status(400).json({ message: "No file uploaded" }); } const folderPath = `Files/`; const fileName = folderPath + file.originalname; const blob = retmBucket.file(fileName); // 创建写入流时传入onProgress配置 const blobStream = blob.createWriteStream({ onProgress: (progressEvent) => { const percent = (progressEvent.bytesWritten / progressEvent.totalBytes) * 100; console.log(`上传进度: ${percent.toFixed(2)}%`); // 若要向客户端实时推送进度,推荐用SSE(Server-Sent Events) res.write(`data: ${JSON.stringify({ progress: percent.toFixed(2) })}\n\n`); } }); blobStream.on("error", (error) => { res.status(500).json({ message: "Error during file upload!", error: error.message }); }); blobStream.on("finish", async () => { // 结束SSE响应并返回最终结果 res.end(JSON.stringify({ message: "File has been uploaded successfully", data: fileName })); }); blobStream.end(file.buffer); }catch(error){ res.status(409).json({ message: "Error during file upload!", error: error.message }) } })
方式2:手动跟踪写入字节数
如果需要更灵活的控制,也可以通过分块写入并监听Writable流的drain事件,结合已写入字节数计算进度:
router.post("/upload-file", multer.single("file"), async (req, res, next) => { try{ const file = req.file; if (!file) { return res.status(400).json({ message: "No file uploaded" }); } const folderPath = `Files/`; const fileName = folderPath + file.originalname; const blob = retmBucket.file(fileName); const blobStream = blob.createWriteStream(); let bytesWritten = 0; const totalBytes = file.size; const chunkSize = 1024 * 1024; // 按1MB分块写入 // 监听缓冲区排空事件,更新进度 blobStream.on("drain", () => { const percent = (bytesWritten / totalBytes) * 100; console.log(`上传进度: ${percent.toFixed(2)}%`); // 同样可通过SSE推送进度给客户端 }); // 分块写入逻辑 let offset = 0; function writeChunk() { while (offset < totalBytes) { const chunk = file.buffer.slice(offset, offset + chunkSize); bytesWritten += chunk.length; offset += chunkSize; // 缓冲区满时停止写入,等待drain事件 if (!blobStream.write(chunk)) { blobStream.once("drain", writeChunk); return; } } blobStream.end(); } writeChunk(); blobStream.on("error", (error) => { res.status(500).json({ message: "Error during file upload!", error: error.message }); }); blobStream.on("finish", async () => { res.status(200).json({ message: "File has been uploaded successfully", data: fileName }) }); }catch(error){ res.status(409).json({ message: "Error during file upload!", error: error.message }) } })
关键注意事项
- 向客户端实时上报进度时,普通HTTP响应只能发送一次结果,因此推荐使用Server-Sent Events(SSE)或WebSocket实现实时推送,上述代码已示例SSE的基础写法。
- 确保
retmBucket是已正确初始化的Google Cloud Storage Bucket实例。 - 第一种方式的
onProgress是库封装的官方实现,已处理分块上传的进度计算,比手动跟踪更可靠。
内容的提问来源于stack exchange,提问作者iEmad00
相关产品推荐
相关产品推荐

