Search code examples
javascriptangularjsnode.jsblobfilesaver.js

Saving an image from Node's readStream with client Javascript


I have been struggling with this one for a while now, and need your guys help! I am trying to streamline the process of downloading a report from my website. With node this is fairly straightforward with a readStream, like so:

router.post('/report', (req, res) => {
    const filename = 'test.png';
    const filePath = path.join(__dirname, filename);
    fs.exists(filePath, exists => {
        if (exists) {
            const stat = fs.statSync(filePath);
            res.writeHead(200, {
                'Content-Type': 'image/png',
                'Content-Length': stat.size,
                'Content-Disposition': 'attachment; filename=' + filename,
            });
            fs.createReadStream(filePath).pipe(res);
        } else {
            res.writeHead(400, { 'Content-Type': 'text/plain' });
            res.end('ERROR File does NOT Exists');
        }
    });
});

Now if I try this with Postman or some other API tester, it works perfectly, the file is downloaded and saved correctly. Now I am struggling to get this to work my front-end. I am currently running AngularJS and have tried to use FileSaver.js as a way to take this data and save it, however it never works. The file is saved, but the data is unreadable, aka the image previewer says the image is broken. I think I am creating the Blob incorrectly?

function exportReport(_id) {
    this.$http
        .post(
            '/api/report',
            { _id },
            {
                headers: {
                    'Content-type': 'application/json',
                    Accept: 'image/png',
                },
            }
        )
        .then(data => {
            console.log(data);
            const blob = new Blob([data], {
                type: 'image/png',
            });
            this.FileSaver.saveAs(blob, 'testing.png');
        });
}

The console log result is as so:

Object {data: "�PNG
↵↵
IHDRRX��iCCPICC Profi…g�x @� @������Z��IEND�B`�", status: 200, config: Object, statusText: "OK", headers: function}

Am I suppose to decode the object.data?


Solution

  • Try adding responseType: 'blob' to the request and omit creating a new blob:

    function exportReport(_id) {
        this.$http
            .post(
                '/api/report',
                { _id },
                {
                    headers: {
                        'Content-type': 'application/json',
                        Accept: 'image/png',
                    },
                    responseType: 'blob'
                }
            )
            .then(data => {
                console.log(data);
                this.FileSaver.saveAs(data.data, 'testing.png');
            });
    }