使用Express和Node.js实现文件批量下载遇问题求助
批量下载MongoDB关联文件的问题解决
问题说明
我开发了一个支持文件上传的站点,文件存储在本地/uploads目录,文件名/ID存储在MongoDB的profile数组中。现尝试根据profile数组中的信息批量下载对应文件,但原有代码运行报错。曾通过res.zip实现单文件下载成功,但无法通过循环实现批量下载,特此求助解决方法。
原有报错代码
app.get('/download/:id', async(req,res) =>{ const {id} = req.params; const staffDoc = await Staff.findById(id); var to_zip = fs.readdirSync(__dirname + `/uploads/${staffDoc.profile}`); const zip = new admzip(); for(let i = 0; i < to_zip.length;i++){ zip.addLocalFile(to_zip[i]) }; const file_after_download = 'downloaded_file.zip'; const data = zip.toBuffer(); res.set('Content-Type', 'application/octet-stream'); res.set('Content-Disposition', `attachment; filename=${file_after_download}`); res.set('Content-Length', data.length); res.send(data); });
曾尝试的单文件下载代码
const x = __dirname+"/uploads/"; res.zip([ { path : x + staffDoc.profile[0], name : staffDoc.profile[0] } ])
问题分析与解决方法
核心问题
- 原有代码错误地将
profile数组直接拼接到路径中,导致fs.readdirSync访问的是不存在的目录(数组转字符串后会变成file1,file2这类无效路径)。 - 添加文件到zip时未使用完整路径,导致admzip找不到目标文件。
方法一:修正admzip实现批量下载
直接遍历profile数组中的文件名,拼接完整路径后添加到zip包,同时加入文件存在性校验避免报错:
const fs = require('fs'); const path = require('path'); const AdmZip = require('adm-zip'); app.get('/download/:id', async(req, res) => { try { const { id } = req.params; const staffDoc = await Staff.findById(id); if (!staffDoc?.profile?.length) { return res.status(404).send('无可用下载文件'); } const zip = new AdmZip(); const uploadsDir = path.join(__dirname, 'uploads'); // 遍历profile数组添加文件 for (const fileName of staffDoc.profile) { const filePath = path.join(uploadsDir, fileName); // 校验文件是否存在 if (fs.existsSync(filePath)) { zip.addLocalFile(filePath); } } const zipBuffer = zip.toBuffer(); res.setHeader('Content-Type', 'application/zip'); res.setHeader('Content-Disposition', `attachment; filename="staff_files.zip"`); res.setHeader('Content-Length', zipBuffer.length); res.send(zipBuffer); } catch (err) { console.error(err); res.status(500).send('下载失败'); } });
方法二:用res.zip实现批量下载
如果你的框架(比如Express的express-zip中间件)支持res.zip,直接将profile数组转换为res.zip要求的对象数组即可:
const path = require('path'); app.get('/download/:id', async(req, res) => { try { const { id } = req.params; const staffDoc = await Staff.findById(id); if (!staffDoc?.profile?.length) { return res.status(404).send('无可用下载文件'); } const uploadsDir = path.join(__dirname, 'uploads'); // 生成res.zip需要的文件数组 const filesToZip = staffDoc.profile.map(fileName => ({ path: path.join(uploadsDir, fileName), name: fileName // 可自定义zip内的文件名,保持原文件名则用fileName })); res.zip(filesToZip, 'staff_files.zip'); } catch (err) { console.error(err); res.status(500).send('下载失败'); } });
注意事项
- 用
path.join代替字符串拼接路径,避免不同系统路径分隔符的兼容问题。 - 添加错误捕获和文件存在性校验,避免因文件缺失或数据库查询失败导致服务崩溃。
- 使用
res.zip时,需确保已安装并配置对应中间件(如express-zip)。
内容的提问来源于stack exchange,提问作者Caleb Bako
相关产品推荐
相关产品推荐

