Upload multiple files to BackBlaze按照原始文件名。

huangapple go评论78阅读模式
英文:

Upload multiple files to BackBlaze according to the original filename

问题

I'll provide the translated code part for your request:

export const uploadToBackBlaze = async (req, res, next) => {
  // Defining backblaze masterkey and application key

  const b2 = new B2({
    applicationKeyId: process.env.BACKBLAZE_MASTER_APPLICATION_KEY_ID,
    applicationKey: process.env.BACKBLAZE_MASTER_APPLICATION_ID,
  });

  const __dirname = path.resolve(); // Defining directory
  let tempDir = path.join(__dirname, "chapterTemp"); // the temp directory

  const imageIds = []; // Empty array where the file IDs will be pushed

  try {
    await b2.authorize().then(async () => {
      const bucketId = process.env.BACKBLAZE_BUCKET_ID;

      fs.readdir(tempDir, async function (err, files) {
        if (err) {
          console.error(err);
          res.status(500).json({ message: err.message });
          return;
        }

        // Unique id

        let uid = uuidv4();

        // Reading file from temp Dir and upload to backblaze

        const uploadPromises = files.map(async (file, index) => {
          const fileData = fs.readFileSync(path.join(tempDir, file));
          const uploadFileName = `${index + 1}.jpg`; // Naming the file in sequential order
          const uploadUrl = await b2.getUploadUrl(bucketId); // Getting upload URL and auth token
          const response = await b2.uploadFile({
            uploadUrl: uploadUrl.data.uploadUrl,
            uploadAuthToken: uploadUrl.data.authorizationToken,
            filename: uploadFileName,
            data: fileData,
            mime: "image/png" || "image/jpg" || "image/jpeg" || "image/webp", // replace with the appropriate MIME type for your files
          });

          imageIds.push(response.data.fileId);
        });

        await Promise.all(uploadPromises);

        req.imageIds = imageIds;
        next();
      });
    });
  } catch (error) {
    return res.status(500).json({ message: error.message });
  }
};

I've modified the code to ensure that the file IDs in the imageIds array correspond to the sequential order of the images, starting from 1.jpg.

英文:

I'm trying to upload images to BackBlaze using backblaze-b2 npm module from a folder that has multiple images and for each image successfully uploading I push the fileId to an array all the files in the folder is named from 0001.jpg to how many files there are in this was if there are 12 images in the folder the last image will be named 0012.jpg but after all images are uploaded and the ids are pushed into the array it'll be like 2nd image's id will place placed on 5th or 6th in the array the last image will be placed at the first

Here is the code I'm using

export const uploadToBackBlaze = async (req, res, next) => {
// Defining backblaze masterkey and application key
const b2 = new B2({
applicationKeyId: process.env.BACKBLAZE_MASTER_APPLICATION_KEY_ID,
applicationKey: process.env.BACKBLAZE_MASTER_APPLICATION_ID,
});
const __dirname = path.resolve(); // Defining directory
let tempDir = path.join(__dirname, "chapterTemp"); // the temp directory
const imageIds = []; // Empty array where the file IDs will be pushed
try {
await b2.authorize().then(async () => {
const bucketId = process.env.BACKBLAZE_BUCKET_ID;
fs.readdir(tempDir, async function (err, files) {
if (err) {
console.error(err);
res.sendStatus(500).json({ message: err.message });
return;
}
// Unique id
let uid = uuidv4();
// Reading file from temp Dir and upload to backblaze
const uploadPromises = files.map(async (file) => {
const fileData = fs.readFileSync(path.join(tempDir, file));
const uploadFileName = path.join(uid, file); // changing file name to uniques
const uploadUrl = await b2.getUploadUrl(bucketId); // Getting upload URL and auth token
const response = await b2.uploadFile({
uploadUrl: uploadUrl.data.uploadUrl,
uploadAuthToken: uploadUrl.data.authorizationToken,
filename: uploadFileName,
data: fileData,
mime: "image/png" || "image/jpg" || "image/jpeg" || "image/webp", // replace with the appropriate MIME type for your files
});
imageIds.push(response.data.fileId);
});
await Promise.all(uploadPromises);
req.imageIds = imageIds;
next();
});
});
} catch (error) {
return res.status(500).json({ message: error.message });
}
};

I want the array to have the file ids in order like 1.jpg's file id should one on the imageIds[0] position and 2.jpg should be on the imageIds[1] position and so on!

答案1

得分: 1

这个问题实际上与Backblaze B2无关,与异步编程有关。您有这段代码:const uploadPromises = files.map(...)(其中...是将文件映射到承诺的代码)。这段代码为每个文件创建一个承诺。这是好的!这是您想要的。您希望某些代码对每个文件执行。您甚至通过让每个承诺立即开始工作来利用Node.js对并发的支持。

然后,您有这段代码:await Promise.all(uploadPromises);,它会阻止程序,直到所有承诺都执行完毕。这也是好的。您可能不希望程序继续执行(向客户端返回HTTP响应),直到所有文件都被处理完毕。

但是,每个文件的处理将以不确定的顺序完成。每次运行程序时,顺序都会不同。这是由于网络IO等因素造成的。没有办法确保0001.jpg的网络请求会在0002.jpg的网络请求之前完成,依此类推。

您可以在下面的示例中看到这种情况:

async function startTasks() {
    let results = [];

    let tasks = ['1', '2', '3'].map((str) => {
        return new Promise(async (resolve) => {
            let delay = Math.floor(Math.random() * 3000) + 1;
            await new Promise(r => setTimeout(r, delay));
            results.push(str);
            resolve();
        });
    });

    await Promise.all(tasks);
    console.log(results);
}

startTasks();

在这里,将1到3之间的数字作为字符串映射到承诺。这些承诺只是等待1到3000毫秒之间的随机时间,设计成模拟使用Backblaze库的工作。

如果运行此代码,您将看到每次完成任务的顺序都不同。

$ node index.js 
[ '1', '3', '2' ]
$ node index.js 
[ '1', '2', '3' ]

要保留任务的顺序,您可以返回每个承诺的结果(字符串标识符),而不是将其推入承诺内部的结果数组。然后,您可以直接将Promise.all的结果分配给结果数组。

async function startTasks() {
    let tasks = ['1', '2', '3'].map((str) => {
        return new Promise(async (resolve) => {
            let delay = Math.floor(Math.random() * 3000) + 1;
            await new Promise(r => setTimeout(r, delay));
            resolve(str);
        });
    });

    let results = await Promise.all(tasks);
    console.log(results);
}

startTasks();

这次,当您打印结果时,顺序将与它们启动的顺序相匹配。

$ node index.js 
[ '1', '2', '3' ]
$ node index.js 
[ '1', '2', '3' ]

请注意,这并不考虑错误处理。如果您将此示例适应到您的用例中,您应该添加错误处理。以下是上述示例的扩展版本,考虑了错误。

async function startTasks() {
    let tasks = ['1', '2', '3'].map((str) => {
        return new Promise(async (resolve, reject) => {
            try {
                let delay = Math.floor(Math.random() * 3000) + 1;
                await new Promise(r => setTimeout(r, delay));

                // 有50%的机会抛出错误
                if (Math.random() < 0.5) {
                    throw new Error(`任务 ${str} 中遇到错误`);
                }

                resolve(str);
            } catch (error) {
                reject(error);
            }
        });
    });

    try {
        let results = await Promise.all(tasks);
        console.log(results);
    } catch (error) {
        console.error('一个或多个任务失败:', error);
    }
}

startTasks();

在这里,有50%的机会会引发错误。当您运行此代码时,它可能会记录错误,也可能不会。

$ node index.js 
一个或多个任务失败: Error: 任务 2 中遇到错误
    at ...
$ node index.js 
一个或多个任务失败: Error: 任务 3 中遇到错误
    at ...

在现实世界中,对于您的用例,这个错误会由您使用的Backblaze库引发。请注意,由于有多个可能引发错误的任务正在执行,您必须像处理成功的任务结果一样收集错误,并处理它们。

或者,您可以更改代码,一旦一个任务失败,就立即失败(甚至不开始其他任务),或者更改代码以按顺序执行任务,保存成功的结果,同时对第一个失败的任务返回错误(甚至不开始其他任务)。

英文:

This problem actually has nothing to do with Backblaze B2. It's a problem related to asynchronous programming. You have the code const uploadPromises = files.map(...) (where ... is the code that maps the files to promises). This code creates a promise for each file. This is good! It's what you want. You want some code to execute for each file. You even take advantage of Node.js support for concurrency by having each promise begin its work immediately.

Then you have the code await Promise.all(uploadPromises); which blocks the program until all of the promises have finished executing. This is also good. You probably don't want your program to continue executing (returning an HTTP response to the client) until all of the files have been dealt with.

But the processing for each file will complete in a nondeterministic order. Each time you run the program, the order will be different. This is because of factors like network IO. There's no way to ensure the network request for 0001.jpg will finish before the network request for 0002.jpg, and so on.

You can see this in action with a simplified version of what your code is doing.

async function startTasks() {
    let results = [];

    let tasks = [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;].map((str) =&gt; {
        return new Promise(async (resolve) =&gt; {
            let delay = Math.floor(Math.random() * 3000) + 1;
            await new Promise(r =&gt; setTimeout(r, delay));
            results.push(str);
            resolve();
        });
    });

    await Promise.all(tasks);
    console.log(results);
}

startTasks();

Here, numbers between 1 and 3, as strings, are mapped to promises. The promises here just wait a random number of milliseconds between 1 and 3000 inclusively, designed to simulate work like using the Backblaze library like your code does.

If you run the code, you'll see that it completes the tasks in a different order each time.

$ node index.js 
[ &#39;1&#39;, &#39;3&#39;, &#39;2&#39; ]
$ node index.js 
[ &#39;1&#39;, &#39;2&#39;, &#39;3&#39; ]

To retain the order of tasks, you could return the result (string identifier) from each promise instead of pushing it into the results array inside the promise. Then, you can directly assign the results of Promise.all to the results array.

async function startTasks() {
    let tasks = [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;].map((str) =&gt; {
        return new Promise(async (resolve) =&gt; {
            let delay = Math.floor(Math.random() * 3000) + 1;
            await new Promise(r =&gt; setTimeout(r, delay));
            resolve(str);
        });
    });

    let results = await Promise.all(tasks);
    console.log(results);
}

startTasks();

This time, when you print the results, the order matches the order they were started in.

$ node index.js 
[ &#39;1&#39;, &#39;2&#39;, &#39;3&#39; ]
$ node index.js 
[ &#39;1&#39;, &#39;2&#39;, &#39;3&#39; ]

Note that this doesn't account for error handling. If you adapted this example for your use case, you should add error handling. Here's an expanded version of the above example that accounts for errors.

async function startTasks() {
    let tasks = [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;].map((str) =&gt; {
        return new Promise(async (resolve, reject) =&gt; {
            try {
                let delay = Math.floor(Math.random() * 3000) + 1;
                await new Promise(r =&gt; setTimeout(r, delay));

                // 50% chance to throw an error
                if (Math.random() &lt; 0.5) {
                    throw new Error(`Error encountered in task ${str}`);
                }

                resolve(str);
            } catch (error) {
                reject(error);
            }
        });
    });

    try {
        let results = await Promise.all(tasks);
        console.log(results);
    } catch (error) {
        console.error(&#39;One or more tasks failed: &#39;, error);
    }
}

startTasks();

Here, there's a 50% chance of an error being thrown. When you run this code, it might log errors, and it might not.

$ node index.js 
One or more tasks failed:  Error: Error encountered in task 2
at ...
$ node index.js 
One or more tasks failed:  Error: Error encountered in task 3
at ...

In the real world, for your use case, this error would be thrown by the Backblaze library you're using. Note that because there's more than one task being executed that could throw an error, you have to collect the errors the same way you would collect results from successful tasks, and then deal with them.

Or, you could change your code to fail as soon as one of the tasks failed (and discard any successful results collected thus far from tasks that hadn't failed). Or, you could change your code to execute the tasks sequentially, saving successful results as they occur, and returning an error for the first task that failed (without even starting the rest).

答案2

得分: 0

以下是您要翻译的内容:

"Doing this fixed the issue and It's been working fine after I tried it multiple times"

"Instead of pushing the Image Ids into an array if I just send the Promise.all(uploadPromises) as req.imageIds the image Ids are resolved into an array in the some order of their original File Name"

英文:

Doing this fixed the issue and It's been working fine after I tried it multiple times

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

import fs from &quot;fs&quot;;
import B2 from &quot;backblaze-b2&quot;;
import path from &quot;path&quot;;
import dotenv from &quot;dotenv&quot;;
import { v4 as uuidv4 } from &quot;uuid&quot;;
dotenv.config();
export const uploadToBackBlaze = async (req, res, next) =&gt; {
// Defining backblaze masterkey and application key
const b2 = new B2({
applicationKeyId: process.env.BACKBLAZE_MASTER_APPLICATION_KEY_ID,
applicationKey: process.env.BACKBLAZE_MASTER_APPLICATION_ID,
});
const __dirname = path.resolve(); // Defining directory
let tempDir = path.join(__dirname, &quot;processed&quot;); // the temp directory
try {
await b2.authorize().then(async () =&gt; {
const bucketId = process.env.BACKBLAZE_BUCKET_ID;
fs.readdir(tempDir, async function (err, files) {
if (err) {
console.error(err);
res.sendStatus(500).json({ message: err.message });
return;
}
// Unique id
let uid = uuidv4();
// Reading file from temp Dir and upload to backblaze
const uploadPromises = files.map(async (file) =&gt; {
const fileData = fs.readFileSync(path.join(tempDir, file));
const uploadFileName = path.join(uid, file); // changing file name to uniques
const uploadUrl = await b2.getUploadUrl(bucketId); // Getting upload URL and auth token
const response = await b2.uploadFile({
uploadUrl: uploadUrl.data.uploadUrl,
uploadAuthToken: uploadUrl.data.authorizationToken,
filename: uploadFileName,
data: fileData,
mime: &quot;image/png&quot; || &quot;image/jpg&quot; || &quot;image/jpeg&quot; || &quot;image/webp&quot;, // replace with the appropriate MIME type for your files
});
return response.data.fileId;
});
req.imageIds = await Promise.all(uploadPromises);
fs.rmdirSync(tempDir, { recursive: true }); // Removing Files From Temp Dir
next();
});
});
} catch (error) {
fs.rmdirSync(tempDir, { recursive: true }); // Removing Files From Temp Dir
return res.status(500).json({ message: error.message });
}
};

<!-- end snippet -->

Instead of pushing the Image Ids into an array if I just send the Promise.all(uploadPromises) as req.imageIds the image Ids are resolved into an array in the some order of their original File Name

huangapple
  • 本文由 发表于 2023年5月14日 19:58:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76247365.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定