1

I think I'm very close to what I want to do. I have the following api get method in node.js that is retrieving a file varbinary(MAX) from an SQL Server database. It was converted from a base64 encoded string before inserted so the Content Type information was stripped from the string.

node.js

router.get('/getFile', (req, res) => {
    console.log("Calling getFile for file " + req.query.serialNumber + ".")
    var serialNumber = req.query.serialNumber;
    let request = new sql.Request(conn);
    request.query('SELECT FileName + \'.\' + FileExtension AS \'File\', FileType, ContentType, SerialNumber, Chart ' +
        'FROM dbo.ChangeFiles ' +
        'WHERE SerialNumber = ' + serialNumber)
        .then(function (recordset) {
            log("Successfully retrieved file " + recordset[0].SerialNumber + " from database.");
            log("Length of blob " + recordset[0].File + " is " + recordset[0].Chart.length)
            res.status(200);
            res.setHeader('Content-Type', recordset[0].ContentType);
            res.setHeader('Content-Disposition', 'attachment;filename=' + recordset[0].File);
            res.end(Buffer.from((recordset[0].Chart)));
        }).catch(function (err) {
            log(err);
            res.status(500).send("Issue querying database!");
        });
});

That works fine, but what to do in Angular to retrieve it and prompt for a download for the user has not been clear for me, nor has there been a lot as far as help/resources online. Here is what I have so far in my service class.

fileDownload.service.ts

downloadFile(serialNumber: string): Observable<any> {
    return this.http.get(this.baseURL + '/getFile', { params: { serialNumber: serialNumber } })
    .map(this.extractFile);
}


private extractFile(response: Response) {
    const file = new Blob([response.blob]);
    FileSaver.saveAs(file);
    // const url = window.URL.createObjectURL(file);
    // window.open(url);
    return file;
}

As you can see I've tried a couple of approaches. The commented out portion of the extractFile method didn't work at all, and using the FileSaver.saveAs function produces a file download of an unknown type, so the headers sent from node.js didn't seem to affect the file itself.

Would someone be able to advise how to proceed in Angular with what is successfully being sent from node.js so that I can successfully download the file, regardless of type?

Thanks so much in advance.

2 Answers 2

1

I got it working afterall. I had to rework the api call so that it sent all of the file information separately so that the MIME type, and file name can be assigned to the file on the client side in the component class. For some reason when I tried to do so all in the api, it wouldn't work so that was my work around. So here is what works for me.

node.js api

router.get('/getFile', (req, res) => {
    console.log("Calling getFile for file " + req.query.serialNumber + ".")
    var serialNumber = req.query.serialNumber;
    let request = new sql.Request(conn);
    request.query('SELECT FileName + \'.\' + FileExtension AS \'File\', FileType, ContentType, SerialNumber, Chart ' +
        'FROM dbo.ChangeFiles ' +
        'WHERE SerialNumber = ' + serialNumber)
        .then(function (recordset) {
            log("Successfully retrieved file " + recordset[0].SerialNumber + " from database.");
            log("Length of blob " + recordset[0].File + " is " + recordset[0].Chart.length)
            res.send(recordset[0]);
        }).catch(function (err) {
            log(err);
            res.status(500).send("Issue querying database!");
        });
});

component class

  downloadFile(serialNumber: string): void {
    this.changeService.downloadFile(serialNumber).subscribe((res: any) => {
      const ab = new ArrayBuffer(res.Chart.data.length);
      const view = new Uint8Array(ab);
      for (let i = 0; i < res.Chart.data.length; i++) {
        view[i] = res.Chart.data[i];
      }
      const file = new Blob([ab], { type: res.ContentType });
      FileSaver.saveAs(file, res.File);
      console.log(res);
    });
  }

service class

downloadFile(serialNumber: string): Observable<any> {
        return this.http.get(this.baseURL + '/getFile', { params: { serialNumber: serialNumber } })
        .map(this.extractFile);
    }

    private extractFile(response: Response) {
        // const file = new Blob([response.blob]);
        // FileSaver.saveAs(file);
        // const url = window.URL.createObjectURL(file);
        // window.open(url);
        const body = response.json();
        return body || {};
    }
Sign up to request clarification or add additional context in comments.

Comments

0

Update your code to call subscribe instead of map

Comments

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.