Reputation: 641
My node server gets a file's contents (base64 string) from an external API. I want the in-memory base64 string to be downloaded by a client as a file. However, the file is corrupt after the client's browser downloads it.
If I take the same file contents, save them to the server's local folder using fs, then send that file to the client, the file is saved properly on the client's system.
When I take a look at the difference between the two sets of hex data, there are differences and similarities. comparison screenshot, valid file is on top
Has anyone conquered this problem before, or have a theory as to why one works and the other doesn't? I want to try to avoid the step of saving the file on my server, if possible.
attachment.Name = 'picture.jpg'; //this can be any type of file
attachment.ContentType = 'image/jpeg'; //just an example with an image
attachment.ContentBytes = 'iVBORw0KGgoAAAAN.....' //long but complete base64 string
Code for serving in-memory download (file is corrupt once on client machine):
note: the below variable contents
's hex representation is identical to the valid file's hex data
var atob = require('atob');
//tell the browser to download this
res.setHeader('Content-disposition', 'attachment; filename=' + attachment.Name);
res.setHeader('Content-type', attachment.ContentType);
var contents = atob(attachment.ContentBytes);
return res.send(200, contents);
also
Code for serving local file download (file is valid once on client machine):
var fs = require("fs");
var directory = "temporary/" + attachment.Name;
//tell the browser to download this
res.setHeader('Content-disposition', 'attachment; filename=' + attachment.Name);
res.setHeader('Content-type', attachment.ContentType);
//save the file on the server temporarily
fs.writeFile(directory, attachment.ContentBytes, 'base64', function(err)
{
if (err)
{
console.log(err);
return res.serverError();
}
});
//send the file to the client
var fileStream = fs.createReadStream(directory);
fileStream.pipe(res);
//once the file is sent, send 200 and delete the file from the server
fileStream.on('end', function ()
{
fs.unlink(directory);
return res.ok();
});
Upvotes: 3
Views: 3333
Reputation: 641
This solved my problem. What gets sent to the client is a buffer of proper bytes, instead of a string of ASCII. The browser seems happy to accept this.
//tell the browser to download this
res.setHeader('Content-disposition', 'attachment; filename=' + attachment.Name);
res.setHeader('Content-type', attachment.ContentType);
//convert to a buffer and send to client
var fileContents = new Buffer(attachment.ContentBytes, "base64");
return res.send(200, fileContents);
Upvotes: 3