10

I tried 400 combinations of syntaxes and headers, I can't figure out how to make a HTTP call from Angular to retrieve a file from my NodeJS server.

Found on Stackoverflow and tried, to no avail :

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

It can't be a simple <a download> tag, or a public express.static() folder, because access to the file is restricted and I need to pass a JWT token along (in Node, I have an Express authentication middleware that will reject the request if no token is provided in the headers or if it is invalid).

The file is a GZIP : ./dumps/dump.gz and weighs 812 Kb.

I do manage to download the file, but whatever I try, it weighs 1.4 MB or 94 bytes (wrong size) and can't be opened (7zip can't open file downloads/dump.gz as archive).

What I have tried Angular-side (multiple attempts) :

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))

What I have tried Node-side (multiple attempts) :

EDIT

Node has been innocented as I could retrieve the file directly from Chrome after disabling authentication. So, the back-end works and the issue is in 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)
})
10
  • did you try using postman to serve the file and see if your backend it working fine? Commented May 24, 2019 at 6:16
  • I don't get what you mean. The backend serves the file, and Postman is a tool to query the backend and retrieve the file from it, isn't it? Not to serve it. It's the other way round, or what did I miss? Commented May 24, 2019 at 6:57
  • 1
    Please test your backend code first. Whether your backend is able to send the file properly. Then look at your frontend angular code Commented May 24, 2019 at 7:25
  • This is exactly my problem. Is the back-end not sending the file properly? Or is Angular not receiving it properly? Because as far as I can tell, in the console, Node does find the file and send something, and in the browser, I do see data coming from the server. So, "something" is being sent from server to browser. But then, what is wrong? Probably some header or encoding somewhere, but I can't figure out which. Commented May 24, 2019 at 8:42
  • Do you get any warning in the console (cors or anything)? Can you deactivate the JWT check and just open the url in a browser to see if you can download a valid file? Commented May 24, 2019 at 10:35

1 Answer 1

15
+50

Assuming that you are using the new HttpClient from angular (available since angular 4), this should work

front

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))

backend

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); 
})
Sign up to request clarification or add additional context in comments.

4 Comments

{ headers, responseType: "blob" } isn't a valid syntax, I think it should be { headers: headers, responseType: "blob" }. But Typescript points out : (property) RequestOptionsArgs.responseType?: ResponseContentType Type 'string' is not assignable to type 'ResponseContentType'.ts(2322) and refuses to compile
I tried that code at work and it worked. I don't think there was any warning about the syntax, but I was using TS 3.4. The message about ResponseContentType is definitely because you are using the old HttpModule
Using the new HttpClient fixed it. I needed some time to update my app because it broke everything, but now it works. Not sure why it was impossible with the old Http module though... but at least the new one works. Thanks @David, here's your bounty reward :)
Thanks :) Yeah it's probably possible using the old one as well, but you're better off with the new one I'm sure

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.