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

Spring Boot后端返回PDF文件,前端jQuery Ajax无法触发下载求助

解决jQuery Ajax请求Spring Boot后端PDF无法触发下载的问题

我来帮你搞定这个PDF下载的坑,核心问题出在jQuery Ajax处理二进制数据的方式以及你当前的响应处理逻辑上。先拆解问题,再给你具体的修改方案:

问题根源分析

  1. 你的sendRequest方法硬把dataType设成了json,但后端返回的是二进制PDF文件——jQuery会把二进制数据当成文本解析,直接破坏了文件的二进制结构,导致拿到的内容无法还原成正常PDF。
  2. 你在回调里尝试解析JSON,但PDF根本不是JSON格式,这一步完全多余,还会干扰后续的下载逻辑。
  3. 虽然你设置了contentType: application/pdf,但dataType的错误配置让整个响应处理方向错了。

解决方案:分两步修改

第一步:修改前端Ajax逻辑,正确处理二进制Blob

把sendRequest改成支持二进制Blob处理的版本,关键是让jQuery不要乱解析响应,而是直接返回Blob对象,再手动触发下载:

myProject.sendRequest = function (url, method, dataGetter, onSuccess, onError, onDone, cache, contentType, noAuth, _form) {
    var params = {
        url: url,
        type: method,
        data: dataGetter ? getData() : null,
        // 核心:告诉jQuery把响应当成二进制Blob处理
        dataType: 'blob',
        contentType: 'application/json;charset=utf-8',
        // 禁止jQuery自动处理请求数据(GET请求时避免多余参数)
        processData: false,
        cache: cache || false
    };
    // 覆盖contentType(比如PDF请求时的application/pdf)
    if (contentType) params.contentType = contentType;

    jQuery.ajax(params)
    .done(function (blobData, status, xhr) {
        // 从响应头提取文件名
        var contentDisposition = xhr.getResponseHeader('Content-Disposition');
        var filename = 'download.pdf';
        if (contentDisposition) {
            // 匹配Content-Disposition里的filename字段
            var match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
            if (match && match[1]) {
                filename = match[1].replace(/['"]/g, '');
            }
        }

        // 创建临时下载链接触发浏览器下载
        const downloadUrl = window.URL.createObjectURL(blobData);
        const aTag = document.createElement('a');
        aTag.href = downloadUrl;
        aTag.download = filename;
        document.body.appendChild(aTag);
        aTag.click();
        // 清理资源,避免内存泄漏
        window.URL.revokeObjectURL(downloadUrl);
        document.body.removeChild(aTag);

        // 执行原本的done回调(如果有)
        if (onDone) onDone();
    })
    .fail(function (xhr, status, error) {
        if (onError) onError(error);
        else execError('Es trat ein interner Fehler auf.');
    })
    .always(function (xhrOrData) {
        // 只对非PDF请求保留原有的JSON检查逻辑
        const isPdfRequest = contentType === 'application/pdf';
        if (!isPdfRequest && xhrOrData?.status) {
            try {
                const json = volante.removeJsonPrefix(xhrOrData.responseText);
                checkResponse(xhrOrData.status, JSON.parse(json));
            } catch (e) {
                // 忽略JSON解析错误
            }
        }
    });
}

这段代码的关键改进:

  • dataType: 'blob':让jQuery直接返回二进制Blob对象,不做任何文本解析
  • 手动从响应头提取文件名,保证下载的文件名和后端指定的一致
  • 创建临时<a>标签触发浏览器的原生下载行为,这是Ajax请求二进制文件后触发下载的标准方式

第二步:优化后端代码(可选但更规范)

你的后端代码已经能输出PDF,但可以做一些更严谨的优化,避免资源泄漏和路径错误:

@GetMapping("/print.do")
public void doPrint(HttpServletResponse response) {
    String filename = "44005001.25759.pdf";
    // 用Paths安全拼接路径,避免手动拼字符串的错误
    Path pdfPath = Paths.get(settings.getPrintPath(), filename);

    try {
        // 设置正确的响应头
        response.setContentType("application/pdf");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Expires", "0");
        // 给文件名加引号,避免特殊字符导致的问题
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");

        // 用try-with-resources自动关闭流,防止资源泄漏
        try (InputStream inputStream = Files.newInputStream(pdfPath);
             ServletOutputStream outputStream = response.getOutputStream()) {
            // 直接用Files.copy高效复制流,替代手动循环读写
            Files.copy(pdfPath, outputStream);
            outputStream.flush();
        }
    } catch (IOException e) {
        LOG.error("WorkListController.doPrint(): Error while reading PDF file.", e);
        // 返回500错误码给前端
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }
}

优化点说明:

  • 用Paths.get()拼接路径,避免Windows/Linux路径分隔符的问题
  • try-with-resources自动关闭流,不用手动写close(),更安全
  • 移除Content-Type里的charset(PDF是二进制文件,字符编码无意义)
  • 给文件名加双引号,避免文件名包含空格或特殊字符时出现解析错误

调用方式保持不变

你原来的调用代码不需要修改:

myProject.sendRequest('print.do', 'GET', null, null, null, null, null, "application/pdf");

这样修改后,点击按钮就能正常触发PDF下载了,核心就是让前端正确处理二进制Blob数据,而不是当成JSON来解析。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:22:00