如何从NodeJS服务器下载文件以在React中使用?(获取的文件已损坏)

3
我希望能够使用Node.js将PDF文件发送到前端。但是当我这样做时,会出现错误,无法打开文件。这是错误信息(翻译的错误信息:加载PDF文档时发生了错误): enter image description here 我认为一切都正常,但仍无法正常工作。
以下是Node.js代码:
routerTrainer.get("/download-training", verifyJWT, async (req, res) => {

  const { training_id } = req.headers;

  let training = await Training.findOne({
    where: { id: training_id },
  });

  if (training) {
   const path = "/home/gonsa02/Disk/Projects/";
   const dirname = "uploads/trainings/";
   res.download(`${path}${dirname}${training.file_id}`);
  }
});

以下是 React 前端代码:

const downloadTraining = async (id) => {
    const fileReader = new FileReader();
    const JWT = new ClassJWT();
    const axiosReq = axios.create();
    await JWT.checkJWT();
    axiosReq
      .get(`${serverPath}/download-training`, {
        headers: {
          training_id: id,
          token: JWT.getToken(),
          responseType: "blob",
        },
      })
      .then((res) => {
        console.log(res);
        fileReader.readAsDataURL(new Blob([res.data]));
      })
      .catch((err) => console.log(err));

    fileReader.addEventListener("loadend", () => {
      const blobString = fileReader.result;
      const link = document.createElement("a");
      link.href = blobString;
      link.setAttribute("download", "file.pdf");
      document.body.appendChild(link);
      link.click();
    });
  };

不要担心所有像verifyJWT或ClassJWT这样的JWT,它们都是JSON Web Token的实现,可以正常工作。

如果有人知道如何修复它,请告诉我。


${path}${dirname}${training.file_id} 可能会产生比预期少的 / 字符。我的第一个怀疑对象是 dirname 没有以斜杠结尾。 - tevemadar
dirname是正确的,我会写变量以免混淆 - Gonsa02
为什么要使用res.download而不是res.sendFile - Parzh from Ukraine
@DimaParzhitsky 因为我已经尝试过了,它也不起作用。 - Gonsa02
4个回答

3

也许您可以尝试这个

fetch("filepath", {
  headers: headers
})
  .then(res => res.blob())
  .then(blob => {
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.setAttribute("download", "file.pdf");
    document.body.appendChild(link);
    link.click();
  })

我没有测试它。


0

首先,您会得到一个带有ID的文件,因此文件名缺少扩展名.pdf。您可以首先尝试此代码并设置自定义名称。

   res.download(`${path}${dirname}${training.file_id}` , "test.pdf);

如果这不起作用,请尝试发送文件而不是下载

var data =fs.readFileSync(`${path}${dirname}${training.file_id}`);
res.contentType("application/pdf");
res.send(data);


请检查两件事情:1. 检查您的最终目录和文件名,是否保存了{id}或者它们有像.pdf这样的扩展名,如果有扩展名,则应将其添加到路径中。2. 将fs.readFileSync放在try catch中,并检查是否有任何错误,然后console.log数据并检查数据是否打印在控制台上。 - amir najafi
最终目录正确,file_id包括文件扩展名。如果我通过Linux终端使用xdg-open打开它,并且从$ {path} $ {dirname} $ {training.file_id}获取路径,则可以正确打开PDF,因此理论上这是正确的。 - Gonsa02
此外,如果我在React前端使用console.log(response.data)接收到以下内容:data: "%PDF-1.5\n%����\n6 0 obj\n<<\n/Length 2168 \n/Filter /FlateDecode\n>>\nstream\nx��ZKs�6\u0012��W�M\u001c�\u0007�\u001b�}�8��+Q���r�... 那么文件已经被发送。 - Gonsa02

0

我认为你需要将响应类型更改为'arraybuffer',responseType:'arraybuffer'至少在类似情况下对我有效。


0
如果有人需要更多的东西,这里有一个React <-> Express的无需维护库的文件下载器核心代码。 点击下载后的React代码:
  let options = {
  method: 'POST',
  headers: {
    "Accept": "application/pdf",
    "Content-Type": "application/json"
  },
}
let res: any = await fetch(serverUrl + url, options)
const blob = await res.blob()
const data = window.URL.createObjectURL(new Blob([blob]))

const link = document.createElement("a");
link.href = data;
link.setAttribute("download", bodyContent.fileName);
document.body.appendChild(link);
link.click();
link.parentNode!.removeChild(link);

在任何路由中使用快递代码:

const fileName = uuidv4()+".pdf"
const invoice = await createInvoice([order], fileName) // could be a filepath like /src/pdfs/abc.pdf

var file = fs.createReadStream(invoice);
var stat = fs.statSync(invoice);
res.setHeader('Content-Length', stat.size);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename='+fileName);
file.pipe(res);
res.on("finish", ()=>{
  if (fs.existsSync(invoice)) { 
    fs.unlinkSync(invoice) //delete file
  }
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接