按原始文件名顺序向BackBlaze批量上传并保持ID数组有序
解决BackBlaze批量上传后fileId顺序与文件名不匹配的问题
问题背景
使用backblaze-b2 npm模块批量上传命名为0001.jpg至00N.jpg的图片,每成功上传一张就将其fileId存入imageIds数组,但上传完成后数组内的fileId顺序混乱,无法与文件名顺序一一对应(如0002.jpg的ID出现在数组第5/6位,最后一张图的ID在首位)。
问题原因
- 异步任务执行顺序不确定:
Promise.all并行执行所有上传任务,每个任务的完成时间受网络等因素影响,imageIds.push()的顺序是任务完成的先后顺序,而非原文件列表的顺序。 - 文件列表未排序:
fs.readdir返回的文件列表默认不保证按文件名排序,可能本身就不是0001.jpg到00N.jpg的顺序。
解决方案
核心思路
- 先对文件列表按文件名排序,确保处理顺序与预期一致;
- 让每个上传Promise返回对应的fileId,利用
Promise.all按原数组顺序返回结果的特性,直接得到与文件名顺序匹配的imageIds数组。
优化后的代码
import { v4 as uuidv4 } from 'uuid'; import fs from 'fs/promises'; // 使用异步API避免阻塞事件循环 import path from 'path'; import B2 from 'backblaze-b2'; export const uploadToBackBlaze = async (req, res, next) => { const b2 = new B2({ applicationKeyId: process.env.BACKBLAZE_MASTER_APPLICATION_KEY_ID, applicationKey: process.env.BACKBLAZE_MASTER_APPLICATION_ID, }); const __dirname = path.resolve(); const tempDir = path.join(__dirname, "chapterTemp"); let imageIds = []; try { await b2.authorize(); const bucketId = process.env.BACKBLAZE_BUCKET_ID; // 读取文件列表并按文件名排序 let files = await fs.readdir(tempDir); files.sort((a, b) => { // 提取文件名中的数字部分进行排序,确保0001.jpg在前 const numA = parseInt(a.match(/\d+/)[0], 10); const numB = parseInt(b.match(/\d+/)[0], 10); return numA - numB; }); const uid = uuidv4(); // 构建上传Promise数组,每个Promise返回对应fileId const uploadPromises = files.map(async (file) => { const filePath = path.join(tempDir, file); const fileData = await fs.readFile(filePath); const uploadFileName = path.join(uid, file); const uploadUrl = await b2.getUploadUrl(bucketId); const response = await b2.uploadFile({ uploadUrl: uploadUrl.data.uploadUrl, uploadAuthToken: uploadUrl.data.authorizationToken, filename: uploadFileName, data: fileData, mime: getMimeType(file) // 动态匹配MIME类型 }); return response.data.fileId; }); // Promise.all按原数组顺序返回结果,直接赋值即可 imageIds = await Promise.all(uploadPromises); req.imageIds = imageIds; next(); } catch (error) { return res.status(500).json({ message: error.message }); } }; // 辅助函数:根据文件后缀获取正确MIME类型 function getMimeType(filename) { const ext = path.extname(filename).toLowerCase(); switch (ext) { case '.jpg': case '.jpeg': return 'image/jpeg'; case '.png': return 'image/png'; case '.webp': return 'image/webp'; default: return 'application/octet-stream'; } }
额外优化说明
- 替换
fs.readFileSync为fs.promises.readFile,避免同步读取阻塞Node.js事件循环; - 修复原代码中MIME类型的错误写法:原代码
"image/png" || "image/jpg" || ...永远只会返回"image/png",改为根据文件后缀动态判断; - 移除不必要的
.then()嵌套,用await简化代码结构,提升可读性。
内容的提问来源于stack exchange,提问作者Shilajit Dutta
相关产品推荐
相关产品推荐

