【问题标题】:NodeJS request times out when sending a archiver file发送存档文件时NodeJS请求超时
【发布时间】:2021-08-07 15:22:09
【问题描述】:

我有一个 NextJS 应用程序,我必须在其中生成大量 QR 码,一次 300,400,500 个。我想把它们放在一个 zip 中,让用户下载它们。这是使用 archiver 库将代码放入 zip 的代码:

  const archive = archiver.create("zip", {});

  let index = 1;
  for (const code of codes) {
    // @ts-expect-error
    const qrCode = new QRCodeStyling({
      nodeCanvas: canvas,
      width: 300,
      height: 300,
      data: code.url
    });

    archive.append(
      await qrCode.download({
        name: "testName",
        extension: "png",
        skipDownload: true,
        buffer: true,
      }),
      {
        name: `${code.name}-${index}.png`,
      },
    );

    index++;
  }

  console.log("before finalizing");
  await archive.finalize();
  console.log("after finalizing");

  return archive;

请求在本地机器上大约需要 10 秒,而在生产环境中,它每次都会超时。该程序有时甚至没有响应,它只是挂在before finalizing..

这就是我将代码发送到前端的方式:

res.setHeader("Content-Disposition", "attachment");

return res.status(200).send(await exportCodes());

请注意,这些 zip 文件通常在 3-5mb 左右,所以我认为大小应该不是问题

【问题讨论】:

  • 网站在生产中给你的错误是什么?
  • @Mallard 'Task timed out after 10.01 seconds',网站托管在 Vercel 平台上。

标签: javascript node.js typescript next.js


【解决方案1】:

调用qrCode.download(...) 时,您创建了一个promise,然后是awaited,并将结果传递给archive.append(...),然后您才能下载下一个二维码。假设下载一个二维码需要 100 毫秒。将其乘以 300,得到 30000 毫秒,即 30 秒——平均。当然,它会超时。

您必须并行下载。创建一堆promise,一次await,然后archive.append(...)一个一个结果:

interface QrCodeDownloaded {
  name: string;
  index: number;

  // I don't know the type of result here, sorry; must be something like Buffer | Stream | string
  result: any;
}
  const archive = archiver.create("zip", {});
  const downloads: Promise<QrCodeDownloaded>[] = [];

  // We have to use classic for-loop here, because we want to have
  // an independent `index` variable for each iteration
  for (let index = 0; index < codes.length; index++) {
    // counting from 0 here
    const code = codes[index];

    const qrCode = new QRCodeStyling({
      nodeCanvas: canvas,
      width: 300,
      height: 300,
      data: code.url
    });

    const download = qrCode.download({
      name: "testName",
      extension: "png",
      skipDownload: true,
      buffer: true,
    });

    // We have to use .then() here, because we want to hold to
    // code names and indexes, but we also don't want to re-iterate through
    // the array again. We can't use `await` here, because this will negate
    // the whole thing, – we want to initiate the next download without
    // awaiting the current download
    downloads.push(download.then((result) => ({
      result,
      name: code.name,
      index: index + 1, // counting from 1 here
    })));
  }

  // This is the most important line, it does the parallelizing
  const results = await Promise.all(downloads);

  for (const { result, name, index } of results) {
    archive.append(result, { name: `${name}-${index}.png` });
  }

  console.log("before finalizing");
  await archive.finalize();
  console.log("after finalizing");

  return archive;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 2011-01-16
    • 2013-06-25
    • 1970-01-01
    • 2012-01-22
    • 2019-10-06
    • 1970-01-01
    相关资源
    最近更新 更多