目录文件复制并返回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
相关产品推荐
相关产品推荐

