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

使用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]
    }
 ])

问题分析与解决方法

核心问题

  1. 原有代码错误地将profile数组直接拼接到路径中,导致fs.readdirSync访问的是不存在的目录(数组转字符串后会变成file1,file2这类无效路径)。
  2. 添加文件到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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 03:27:56