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

如何在JavaScript+C# ASP.NET中实现批量PDF文件下载?

批量生成PDF无法触发下载,仅保存到服务器目录

我能成功生成并下载单个PDF,但批量生成多个PDF时出问题。用的是单个下载时的相同代码,现在PDF只存到指定位置,不会触发浏览器下载。


前端JavaScript代码

function GeneratePackingSlipAction() {
    var checkedVals = GetSelectedOrders();

    if (checkedVals.length > 0) {
        $.ajax({
            url: '/Orders/PackingSlipBulkOrders',
            type: 'POST',
            data: { selectedOrderIds: checkedVals }
        }).done(function (data) {
            console.log(data);
            alert("was heree:" + data.status);
            if (data.status == true) {
                var msg = "Generated Packing Slip " + data.successCount + " order(s)";
                alert(msg);
                jQuery(this).removeClass("checkedall");
                jQuery(".SelectOrder").prop("checked", false);
            }
        });
    }
}

后端C#控制器代码

public ActionResult PackingSlipBulkOrders(long[] selectedOrderIds)
{
    int successCount = 0;
    int failedCount = 0;

    if (selectedOrderIds.Count() > 0)
    {
        foreach (var id in selectedOrderIds)
        {
            try
            {
                Order order = _orderMgr.GetOrder(id);
                var result = GenerateExcelPDFBulk(order);
                successCount++;
            }
            catch
            {
                failedCount++;
            }
        }
    }
    return Json(new { status = true, successCount = successCount, failedCount = failedCount });
}

public bool GenerateExcelPDFBulk(Order order)
{
    PDFHelperPackingSlip _pdfHelper = new PDFHelperPackingSlip();
    string footerimgpath = Server.MapPath("~/Content/images/dark-logo.png");
    //Create Excel File and Save
    string FileName = "PackingSlip.pdf";
    string fileLocation = Server.MapPath("~/Content/Imports/") + FileName;
    if (System.IO.File.Exists(fileLocation))
    {
        System.IO.File.Delete(fileLocation);
    }
    string filename = _pdfHelper.GeneratePDF(order, fileLocation, footerimgpath);

    Response.ContentType = "application/pdf";
    Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(fileLocation));
    Response.TransmitFile(fileLocation);
    Response.Flush();
    Response.Close();
    Response.End();

    return true;
}

问题原因及修复方案

核心问题

  1. AJAX无法触发浏览器下载:浏览器出于安全限制,不会自动处理AJAX响应的文件流,所以后端设置的下载头无法生效。
  2. 文件名重复导致覆盖:循环生成PDF时使用固定文件名PackingSlip.pdf,后续文件会直接覆盖之前的,最终仅保留最后一个PDF。
  3. Response.End()中断请求:该方法会直接终止当前HTTP请求,导致循环提前结束,且AJAX请求被强制中断。

修复步骤

1. 后端修改:生成ZIP压缩包(批量下载最优方案)

把所有生成的PDF打包成ZIP文件返回,既避免多次下载请求,也解决文件覆盖问题。

修改后的C#代码:

public ActionResult PackingSlipBulkOrders(long[] selectedOrderIds)
{
    int successCount = 0;
    int failedCount = 0;
    var pdfFiles = new List<string>();
    string tempDir = Server.MapPath("~/Content/Imports/TempBulkPDFs/");
    
    // 创建临时目录存储批量PDF
    if (!Directory.Exists(tempDir))
    {
        Directory.CreateDirectory(tempDir);
    }

    if (selectedOrderIds.Any())
    {
        foreach (var id in selectedOrderIds)
        {
            try
            {
                Order order = _orderMgr.GetOrder(id);
                // 用订单ID做文件名后缀,保证唯一性
                string fileName = $"PackingSlip_Order{id}.pdf";
                string fileLocation = Path.Combine(tempDir, fileName);
                PDFHelperPackingSlip _pdfHelper = new PDFHelperPackingSlip();
                string footerimgpath = Server.MapPath("~/Content/images/dark-logo.png");
                
                if (System.IO.File.Exists(fileLocation))
                {
                    System.IO.File.Delete(fileLocation);
                }
                _pdfHelper.GeneratePDF(order, fileLocation, footerimgpath);
                pdfFiles.Add(fileLocation);
                successCount++;
            }
            catch
            {
                failedCount++;
            }
        }
    }

    // 生成ZIP包并返回
    if (pdfFiles.Any())
    {
        string zipFileName = $"BulkPackingSlips_{DateTime.Now:yyyyMMddHHmmss}.zip";
        string zipPath = Path.Combine(tempDir, zipFileName);
        
        using (var zipArchive = ZipFile.Open(zipPath, ZipArchiveMode.Create))
        {
            foreach (var pdfFile in pdfFiles)
            {
                zipArchive.CreateEntryFromFile(pdfFile, Path.GetFileName(pdfFile));
            }
        }

        // 设置下载响应头
        Response.ContentType = "application/zip";
        Response.AppendHeader("Content-Disposition", $"attachment; filename={zipFileName}");
        Response.TransmitFile(zipPath);
        Response.Flush();
        
        // 清理临时文件(可选,也可定时任务清理)
        foreach (var file in pdfFiles)
        {
            System.IO.File.Delete(file);
        }
        System.IO.File.Delete(zipPath);
        Directory.Delete(tempDir, true);
        
        return new EmptyResult();
    }
    
    // 无PDF生成时返回状态
    return Json(new { status = successCount > 0, successCount = successCount, failedCount = failedCount });
}

// 修改生成方法,仅负责生成PDF文件,不处理响应
public bool GenerateExcelPDFBulk(Order order, string fileLocation, string footerimgpath)
{
    PDFHelperPackingSlip _pdfHelper = new PDFHelperPackingSlip();
    if (System.IO.File.Exists(fileLocation))
    {
        System.IO.File.Delete(fileLocation);
    }
    _pdfHelper.GeneratePDF(order, fileLocation, footerimgpath);
    return true;
}

2. 前端修改:替换AJAX为表单提交

AJAX无法触发下载,改用表单提交让浏览器直接处理后端返回的文件流:

function GeneratePackingSlipAction() {
    var checkedVals = GetSelectedOrders();

    if (checkedVals.length > 0) {
        // 创建临时表单
        var form = $('<form>', {
            'method': 'POST',
            'action': '/Orders/PackingSlipBulkOrders'
        });
        
        // 添加选中的订单ID为隐藏字段
        checkedVals.forEach(function(id) {
            $('<input>', {
                'type': 'hidden',
                'name': 'selectedOrderIds',
                'value': id
            }).appendTo(form);
        });
        
        // 提交表单后移除
        form.appendTo('body').submit().remove();
    }
}

额外注意事项

  • 临时文件清理:建议添加服务器定时任务,定期清理临时目录,避免磁盘空间占用。
  • 错误处理:可在后端捕获具体异常类型,返回更详细的错误信息给前端提示用户。
  • 文件名唯一性:始终保证生成的文件名称唯一,避免文件覆盖丢失数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:45:06