使用for循环上传多文件到Google Cloud Storage时如何等待每个上传完成
问题原因
你遇到的问题是Node.js异步回调机制导致的:blobStream的finish、error事件回调都是异步执行的,同步的for循环不会等待这些回调触发,会直接遍历完所有文件后立刻执行最后的res.send逻辑,此时urlList还没有被异步回调填充数据,所以返回为空。
解决方案
将单个文件的上传逻辑封装为返回Promise的函数,在for循环中通过await等待当前文件的上传Promise执行完成(包括上传完成、设置公开权限成功)后,再进入下一轮循环。
完整修改后代码
const processFile = require('../middleware'); const { format } = require('util'); let uuidv4 = require('uuid/v4'); const Cloud = require('@google-cloud/storage'); const { Storage } = Cloud; const storage = new Storage({ keyFilename: './xxx.json', projectId: 'xxx' }); const bucket = storage.bucket('xxx'); // 封装单个文件上传逻辑,返回Promise const uploadSingleFile = (file) => { return new Promise(async (resolve, reject) => { const { originalname, buffer } = file; let filename = originalname .toLowerCase() .split(' ') .join('-'); filename = uuidv4() + '-' + filename; const blob = bucket.file(filename); const blobStream = blob.createWriteStream({ resumable: false }); blobStream.on('error', err => { reject(err); }); blobStream.on('finish', async () => { const publicUrl = format(`https://storage.googleapis.com/${bucket.name}/${blob.name}`); try { await blob.makePublic(); resolve(publicUrl); } catch (err) { console.log('failed to make it public'); reject(err); } }); blobStream.end(buffer); }) } exports.upload = async (req, res) => { const urlList = []; try { await processFile(req, res); //multer if (!req.files || req.files.length === 0) { return res.status(400).send({ message: 'Please upload a file!' }); } // 串行等待每个文件上传完成 for (let i = 0; i < req.files.length; i++) { const fileUrl = await uploadSingleFile(req.files[i]); urlList.push(fileUrl); } return res.status(200).send({ message: 'Uploaded the files successfully', url: urlList }); } catch (err) { return res.status(500).send({ message: err.message }); } };
额外说明
如果不需要严格按顺序上传,想要提升上传速度,可以改用Promise.all并行处理所有文件上传:
// 把上面的串行for循环替换为下面的代码即可 const uploadPromises = req.files.map(file => uploadSingleFile(file)); const urlList = await Promise.all(uploadPromises);
内容的提问来源于stack exchange,提问作者ssaqan
相关产品推荐
相关产品推荐

