Node + Angular:使用 HTTP 请求下载文件

Posted

技术标签:

【中文标题】Node + Angular:使用 HTTP 请求下载文件【英文标题】:Node + Angular : download a file with HTTP request 【发布时间】:2019-10-06 17:33:30 【问题描述】:

我尝试了 400 种语法和标头组合,但我不知道如何从 Angular 发出 HTTP 调用以从我的 NodeJS 服务器检索文件。

在 *** 上找到并尝试过,但无济于事:

Download file from http post request - Angular 6

How download a file from HttpClient

Download a file from NodeJS Server using Express

How do I download a file with Angular2

它不能是简单的<a download> 标记或公共express.static() 文件夹,因为对文件的访问受到限制,我需要传递一个 JWT 令牌(在 Node 中,我有一个 Express 身份验证中间件如果标头中未提供令牌或令牌无效,将拒绝请求。

文件是 GZIP:./dumps/dump.gz,大小为 812 Kb。

我确实设法下载了该文件,但无论我尝试什么,它都重 1.4 MB 或 94 字节(大小错误)并且无法打开 (7zip can't open file downloads/dump.gz as archive)。

我尝试过的Angular-side(多次尝试):

import  saveAs  from 'file-saver';

let headers = new Headers(
    "Authorization": "Bearer " + user.jwt, // I need this in the headers

    "Content-Type" : "application/octet-stream", // Tried with and without, "application/gzip", "application/json", no difference

    "responseType": "blob" as "json", // Tried with and without, "text", "json", no difference

    "Access-Control-Expose-Headers" : "Content-Disposition" // Tried with and without, no difference
)

this.http
    .get("/download/dump",  headers )
    .toPromise()
    .then(res => 

        const blob = new Blob([res["_body"]] ,  type: "application/octet-stream;" );  // Error : body is not a blob or an array buffer
        // const blob = new Blob([res["_body"]]); // Same result
        // const blob = new Blob([res.blob()]); // Error : body is not a blob or an array buffer

        saveAs(blob, "dump.gz"); // Saves a big corrupted file

        // window.URL.createObjectURL(new Blob(blob, type: 'blob')); Saves a 94 byte corrupted file. Tried type: 'gzip', same thing
    )
    .catch(err => console.error("download error = ", err))

我尝试过的节点端(多次尝试):

编辑

Node 已被清除,因为我可以在禁用身份验证后直接从 Chrome 检索文件。所以,后端工作正常,问题出在 Angular 上。

app.get( "/download/dump", authenticate, (req:Request, res:Response) => 
    const file = path.resolve(__dirname, `./dumps/dump.gz`);

    res
        .set( // Tried with and without headers, doesn't seem to do anything
            "Content-Disposition" : "attachment",  // Tried with and without
            "filename" : "dump.gz", // Tried with and without
            "filename*" : "dump.gz",  // Tried with and without
            "Content-Encoding" : "gzip",  // Tried with and without
            "Content-Type" : "application/gzip"  // Tried with and without, "application/text", "application/json", no difference 
        )
        .sendFile(file); // getting a big corrupted file
        // .download(file); // Same result (big corrupted file)
)

【问题讨论】:

您是否尝试过使用邮递员提供文件并查看您的后端是否工作正常? 我不明白你的意思。后端服务于文件,而 Postman 是一个查询后端并从中检索文件的工具,不是吗?不是为它服务。反过来说,还是我错过了什么? 请先测试您的后端代码。您的后端是否能够正确发送文件。然后看看你的前端角码 这正是我的问题。后端没有正确发送文件吗?还是 Angular 没有正确接收它?因为据我所知,在控制台中,Node 确实找到了文件并发送了一些东西,而在浏览器中,我确实看到了来自服务器的数据。因此,“某物”正在从服务器发送到浏览器。但是,有什么问题呢?可能是某处的某些标头或编码,但我不知道是哪个。 您是否在控制台中收到任何警告(cors 或任何东西)?你可以停用 JWT 检查并在浏览器中打开 url 看看是否可以下载有效文件? 【参考方案1】:

假设您正在使用来自 angular 的新 HttpClient(从 angular 4 开始可用),这应该可以工作

正面

import  saveAs  from 'file-saver';
import HttpHeaders from "@angular/common/http";

let headers = new HttpHeaders(
    "Authorization": "Bearer " + user.jwt, // Auth header
    //No other headers needed
);

this.http
    .get("/download/dump",  headers, responseType: "blob" ) //set response Type properly (it is not part of headers)
    .toPromise()
    .then(blob => 
        saveAs(blob, "dump.gz"); 
    )
    .catch(err => console.error("download error = ", err))

后端

app.get( "/download/dump", authenticate, (req:Request, res:Response) => 
    const file = path.resolve(__dirname, `./dumps/dump.gz`);
    //No need for special headers
    res.download(file); 
)

【讨论】:

headers, responseType: "blob" 不是一个有效的语法,我认为它应该是 headers: headers, responseType: "blob" 。但是 Typescript 指出:(property) RequestOptionsArgs.responseType?: ResponseContentType Type 'string' is not assignable to type 'ResponseContentType'.ts(2322) 并拒绝编译 我在工作中尝试了该代码并且它有效。我认为语法没有任何警告,但我使用的是 TS 3.4。关于ResponseContentType的消息肯定是因为你使用的是旧的HttpModule 使用新的 HttpClient 修复了它。我需要一些时间来更新我的应用程序,因为它破坏了一切,但现在它可以工作了。不知道为什么旧的 Http 模块是不可能的……但至少新的模块可以工作。谢谢@David,这是你的赏金奖励:) 谢谢 :) 是的,旧的可能也可以,但我相信你最好用新的

以上是关于Node + Angular:使用 HTTP 请求下载文件的主要内容,如果未能解决你的问题,请参考以下文章

从 Angular 2 客户端到 Node.js 服务器的 HTTP POST 请求

Angular $http POST 从 ExpressJS 获取 null

无法在 Node.js res.body 中获取 $http.post 请求数据

错误状态:使用 Node.js (express)、Angular 和 MongoDB 的 PUT 请求返回 404

Angular HTTP Post 请求,Payload 嵌套在 JSON 对象中

Node js HTTP 服务器/静态 Servlet POST 请求