You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按原始文件名顺序向BackBlaze批量上传并保持ID数组有序

解决BackBlaze批量上传后fileId顺序与文件名不匹配的问题

问题背景

使用backblaze-b2 npm模块批量上传命名为0001.jpg至00N.jpg的图片,每成功上传一张就将其fileId存入imageIds数组,但上传完成后数组内的fileId顺序混乱,无法与文件名顺序一一对应(如0002.jpg的ID出现在数组第5/6位,最后一张图的ID在首位)。

问题原因

  1. 异步任务执行顺序不确定:Promise.all并行执行所有上传任务,每个任务的完成时间受网络等因素影响,imageIds.push()的顺序是任务完成的先后顺序,而非原文件列表的顺序。
  2. 文件列表未排序:fs.readdir返回的文件列表默认不保证按文件名排序,可能本身就不是0001.jpg到00N.jpg的顺序。

解决方案

核心思路

  1. 先对文件列表按文件名排序,确保处理顺序与预期一致;
  2. 让每个上传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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 03:10:01