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

目录文件复制并返回Zip的实现问题:首次Fetch返回空压缩包

问题:首次请求返回空Zip包,二次请求才正常

项目结构

./theme-generator
│
└───netlify/functions/generator
│    │   generator.js
│
└───template
│    │   index.php
│
└───theme
│    │   index.php
│
└───app.js

流程说明

app.js 通过 fetch() 请求 generator.js,后者负责将 /template 目录的内容复制到 /theme 目录,然后生成并返回该目录的 Zip 压缩包。

问题现象

首次发起 fetch() 请求时,返回的是空Zip包;第二次请求时,才能获取到包含正确内容的Zip包。

问题原因分析

核心问题是异步操作未正确等待:generator.js 中复制文件到 /theme 的操作是异步的,但代码没有等待复制完成就执行了压缩逻辑。首次请求时,压缩操作触发时 /theme 目录还没有被写入内容,所以生成空包;第二次请求时,第一次的异步复制操作已经完成,/theme 目录已有内容,因此压缩包正常。

解决方案

1. 确保复制完成后再压缩

使用 async/await 处理文件系统的异步操作,保证所有文件复制完成后再执行压缩步骤。示例代码修改如下:

// 引入fs的promise版本
const fs = require('fs').promises;
const path = require('path');
const archiver = require('archiver');

exports.handler = async (event, context) => {
  const templateDir = path.join(__dirname, '../../../template');
  const themeDir = path.join(__dirname, '../../../theme');
  const zipPath = path.join(__dirname, '../../../theme.zip');

  try {
    // 先清空theme目录(避免残留旧内容)
    await fs.rm(themeDir, { recursive: true, force: true });
    await fs.mkdir(themeDir, { recursive: true });

    // 递归复制template目录内容到theme
    const copyFiles = async (src, dest) => {
      const entries = await fs.readdir(src, { withFileTypes: true });
      for (const entry of entries) {
        const srcPath = path.join(src, entry.name);
        const destPath = path.join(dest, entry.name);
        if (entry.isDirectory()) {
          await fs.mkdir(destPath, { recursive: true });
          await copyFiles(srcPath, destPath);
        } else {
          await fs.copyFile(srcPath, destPath);
        }
      }
    };
    await copyFiles(templateDir, themeDir);

    // 生成Zip包并返回
    const output = fs.createWriteStream(zipPath);
    const archive = archiver('zip', { zlib: { level: 9 } });

    return new Promise((resolve, reject) => {
      output.on('close', async () => {
        const zipBuffer = await fs.readFile(zipPath);
        resolve({
          statusCode: 200,
          headers: {
            'Content-Type': 'application/zip',
            'Content-Disposition': 'attachment; filename="theme.zip"'
          },
          body: zipBuffer.toString('base64'),
          isBase64Encoded: true
        });
        // 清理临时Zip文件
        await fs.unlink(zipPath);
      });

      archive.on('error', (err) => {
        reject({ statusCode: 500, body: err.message });
      });

      archive.pipe(output);
      archive.directory(themeDir, false);
      archive.finalize();
    });
  } catch (err) {
    return { statusCode: 500, body: err.message };
  }
};

2. 避免依赖残留文件

每次请求时先清空 /theme 目录再执行复制,防止之前请求的残留内容干扰,同时确保每次生成的Zip包都是最新的。

3. 正确处理压缩流的结束事件

压缩操作依赖流的close事件,必须等待该事件触发后再读取并返回Zip文件,确保压缩已完成。

内容的提问来源于stack exchange,提问作者Joseph Farruggio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 22:36:33