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

Express.js+EJS项目PDF下载异常:获undefined文本文件问题排查

Express下载PDF返回undefined文本文件的问题

使用Express.js框架实现服务器文件下载,前端EJS页面点击a标签后,未触发目标PDF下载,反而得到内容为“undefined”的文本文件,相关代码及问题修复如下:


客户端代码

HTML(EJS)

<a href="javascript:void(0);" class="download-file" data-id="<%= book._id%>">Download Book</a>

前端脚本

const getFile = document.querySelector("a.download-file");
getFile.addEventListener("click", () => {
    const endPoint = `/pages/${getFile.dataset.id}`;
    if (confirm(`Confirm PDF download to your computer`)) {
        fetch(endPoint, {method: "GET"})
        .then((response) => response.blob())
        .then((res) => {
            console.log(res)
            const url = window.URL.createObjectURL(new Blob([res.data]));
            const link = document.createElement("a");
            link.href = url;
            link.setAttribute("download", "Pdf Book");
            document.body.appendChild(link);
            link.click();
        })
        .catch((err) => console.log("Error:", err));
    } else {
        return;
    }
});

后端代码

路由

bookRouter.get("/:id", bookController.downloadSingleBook);

控制器

const downloadSingleBook = (req, res) => {
    let bookID = req.params.id;
    Book.findById(bookID)
        .then((response) => {
            const file = path.resolve(response.bookfile);
            res.download(file);
        })
        .catch((err) => {
            console.log(err);
        });
};

问题原因及修复

1. 前端Blob处理错误

response.blob()返回的是完整Blob对象,不存在.data属性,你错误访问res.data导致传入Blob的内容为undefined,因此生成的文件内容就是undefined。

修复后的前端脚本:

const getFile = document.querySelector("a.download-file");
getFile.addEventListener("click", () => {
    const endPoint = `/pages/${getFile.dataset.id}`;
    if (confirm(`Confirm PDF download to your computer`)) {
        fetch(endPoint, {method: "GET"})
        .then((response) => {
            // 先判断响应是否成功
            if (!response.ok) throw new Error("File download failed");
            return response.blob();
        })
        .then((blob) => {
            const url = window.URL.createObjectURL(blob);
            const link = document.createElement("a");
            link.href = url;
            // 可从响应头获取服务器返回的文件名,或自定义
            const filename = response.headers.get('content-disposition')?.split('filename=')[1] || "Pdf Book.pdf";
            link.setAttribute("download", filename);
            document.body.appendChild(link);
            link.click();
            // 释放URL对象避免内存泄漏
            window.URL.revokeObjectURL(url);
            document.body.removeChild(link);
        })
        .catch((err) => console.log("Error:", err));
    }
});

2. 后端潜在问题排查

  • 确保response.bookfile是正确的文件路径,path.resolve处理后能指向真实存在的PDF文件
  • 添加文件存在性检查,避免无效路径导致的静默错误:
const fs = require('fs');
const downloadSingleBook = (req, res) => {
    let bookID = req.params.id;
    Book.findById(bookID)
        .then((response) => {
            const file = path.resolve(response.bookfile);
            if (!fs.existsSync(file)) {
                return res.status(404).send("Target file not found");
            }
            res.download(file);
        })
        .catch((err) => {
            console.log(err);
            res.status(500).send("Server error");
        });
};

3. 简化方案(可选)

无需使用fetch,直接让a标签跳转即可实现下载,代码更简洁:

<a href="/pages/<%= book._id%>" class="download-file" download>Download Book</a>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 14:16:46