I have a requirement to download an excel file in the browser. I am using nodejs in the back end and plain JavaScript in the front end.
My API endpoint is 'https://callerid-lookup.herokuapp.com/download'
/download/excel
res.status(200);
var path = './report.xlsx';
fs.readFile(path, function (err, data) {
res.setHeader('Content-disposition', 'attachment; filename=report.xlsx');
res.setHeader('Content-type', 'application/vnd.ms-excel');
console.log('server side1', data);
res.send(data);
});
Front end code looks like following
$.get("https://callerid-lookup.herokuapp.com/download", {
responseType : 'ArrayBuffer'
},
function (data, status) {
console.log("Data: " + data + "\nStatus: " + status);
var blob = new Blob([data], {
type : 'application/vnd.ms-excel'
});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "report.xlsx";
link.click();
});
I can see the file downloading, but there is only [Object, Object] in the excel file downloaded in the first cell.
I tried giving 'responseType' as 'arraybuffer/blog' in the http request, but nothing worked for me, help is appreciated
Log file:
client receive data: https://www.dropbox.com/s/w46o9gqt7z99bzt/client-response.txt?dl=0
server sending data: https://www.dropbox.com/s/xu7nzelbjbmzjwj/server-response.txt?dl=0
Updated with Sample API endpoint and jquery UI code
Related
I'm downloading a file from a server. There I'm setting a file name which I would like to read on a frontend. Here is how I'm doing it on server:
string fileName = "SomeText" + DateTime.UtcNow.ToString() + ".csv";
return File(stream, "text/csv", fileName);
Basically I will return different types of files, sometimes csv sometimes xlsxand sometimes pdf. So I want to get filename on a frontend so I might save it as it should be for example SomeText.pdf it will be saved automatically on local machine as pdf.
Here is my frontend code:
getFileFromServer(params).then(response => {
console.log('server response download:', response);
const type = response.headers['content-type'];
const blob = new Blob([response.data], { type: type, encoding: 'UTF-8' });
//const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'file.xlsx'; // Here I want to get rid of hardcoded value instead I want filename from server
link.click();
link.remove(); // Probably needed to remove html element after downloading?
});
I saw in Network tab under Response Headers that there is a Content-Disposition which holds that info:
Content-Disposition: attachment; filename="SomeText22/08/2019 10:42:04.csv";
But I don't know is it available in my response and how can I read it in my frontend part so I might replace
link.download = 'file.xlsx';
with Path from a server ..
Thanks a lot guys
Cheers
this is a special type of header so to get this header in frontend the backend person should allow from his end. then you can the headers in the response
response.headers.get("content-disposition")
the below code I have used in my project
downloadReport(url, data) {
fetch("url of api")
.then(async response => {
const filename = response.headers
.get("content-disposition")
.split('"')[1];
const text = await response.text();
return { filename, text };
})
.then(responseText => this.download(responseText))
.catch(error => {
messageNotification("error", error.message);
throw new Error(error.message);
});
}
You can get filename from Content-Disposition header by this way
getFileFromServer(params).then(response => {
// your old code
const [, filename] = response.headers['content-disposition'].split('filename=');
const link = document.createElement('a');
link.download = filename;
// your old code
});
Hope this will help
To get the filename from Content-Disposition on client side(frontend) you must give permission from server side(Backend). Spring users can use
#CrossOrigin(value = {"*"}, exposedHeaders = {"Content-Disposition"})
#Controller
#RequestMapping("some endpoint")
in their controller class.
Then from frontend we can get filename using.
const filename = response.headers['content-disposition'].split('filename=')[1];
Hope this helps. Above solution worked fine for me
I'm trying to do a post request onto my api, the api works perfectly ( I am able to post files, but not through a url), but now I'm trying to post through an url.
this is the code I have now, I removed some lines that aren't relevant to the question or were for testing.
request({
url: url + "gettoken"
, json: true
}, function (error, response, body) {
user = body;
var rs = fs.createReadStream(up.url);
var ws = request.post(url + "upload?token=" + `${user.token}&key=${user.key}&filename=${filename}`);
ws.on('drain', function () {
rs.resume();
});
rs.on('end', function () {
console.log(filename);
});
ws.on('error', function (err) {
console.error('cannot send file ' + err);
});
rs.pipe(ws);
})
Can anyone please help me.
So the idea is to upload a file that's located at up.url to another server at url + "upload?...".
Since fs.createReadStream is meant to read local files, and not URL's, you need something that can create a stream from a URL (or rather, retrieve that URL and stream the response).
You can also use request for that:
request({
url: url + "gettoken",
json: true
}, function (error, response, body) {
const user = body;
const rs = request.get(up.url);
const ws = request.post(url + "upload?token=" + `${user.token}&key=${user.key}&filename=${filename}`);
rs.on('end', function () {
console.log(filename);
});
ws.on('error', function (err) {
console.error('cannot send file ' + err);
});
rs.pipe(ws);
});
Typically, file uploads work through multipart/form-data, but your code doesn't suggest that being used here. If it is, the code would become something like this:
const ws = request.post(url + "upload?token=" + `${user.token}&key=${user.key}&filename=${filename}`, {
formData : {
the_file : rs
}
});
// no `rs.pipe(ws)`
I have a simple webapp which I intend to serve a file download from a REST api. The file is of type .xlsx.
My naive attempt to accomplish this uses a design pattern that I have copied from other data pulls from the REST api for example:
var requestData = JSON.stringify({level: plevel, yearMonthStart: beginningYearmo, yearMonthEnd: endingYearmo});
var url = 'http://localhost:8181/v2/file/download';
d3.request(url)
.header("X-Requested-With", "XMLHttpRequest")
.header("Content-Type", "application/octet-stream")
.mimeType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
.send("POST",
requestData,
function(rawData){
console.log(rawData);
});
The response from the server is a 415 error code (unsupported payload media type).
I have tried to set the appropriate headers for the filestream as can be seen above.
My expected behaviour is that the request is accepted without error and the browser initiates a file download. Any guidance here on how to better accomplish this would be appreciated.
I found some examples in other posts that light the way.
You can do this using blob files. Here's an example:
function downloadReport(level, beginningYearmo, endingYearmo){
var requestData = JSON.stringify({plevel: level, yearMonthStart: beginningYearmo, yearMonthEnd: endingYearmo});
var url = 'http://localhost:8181/file/download';
d3.request(url)
.header("X-Requested-With", "XMLHttpRequest")
.header("Content-Type", "application/json")
.header("Accept","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
.header("Cache-Control", "no-cache")
.header("Accept-Charset", "utf-8")
.on("load", function(data){
var blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
console.log("Download request was successful.");
})
.on("error", function(error){ alert("Error: ", error) })
.send("POST", requestData);
}
I'm certain I'm missing something obvious, but the gist of the problem is I'm receiving a PNG from a Mapbox call with the intent of writing it to the file system and serving it to the client. I've successfully relayed the call, received a response of raw data and written a file. The problem is that my file ends up truncated no matter what path I take, and I've exhausted the answers I've found skirting the subject. I've dumped the raw response to the log, and it's robust, but any file I make tends to be about a chunk's worth of unreadable data.
Here's the code I've got at present for the file making. I tried this buffer move as a last ditch after several failed and comparably fruitless iterations. Any help would be greatly appreciated.
module.exports = function(req, res, cb) {
var cartography = function() {
return https.get({
hostname: 'api.mapbox.com',
path: '/v4/mapbox.wheatpaste/' + req.body[0] + ',' + req.body[1] + ',6/750x350.png?access_token=' + process.env.MAPBOX_API
}, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
var mapPath = 'map' + req.body[0] + req.body[1] + '.png';
var map = new Buffer(body, 'base64');
fs.writeFile(__dirname + '/client/images/maps/' + mapPath, map, 'base64', function(err) {
if (err) throw err;
cb(mapPath);
})
})
});
};
cartography();
};
It is possible to rewrite your code in more compact subroutine:
const fs = require('fs');
const https = require('https');
https.get(url, (response)=> { //request itself
if(response) {
let imageName = 'image.png'; // for this purpose I usually use crypto
response.pipe( //pipe response to a write stream (file)
fs.createWriteStream( //create write stream
'./public/' + imageName //create a file with name image.png
)
);
return imageName; //if public folder is set as default in app.js
} else {
return false;
}
})
You could get original name and extension from url, but it safer to generate a new name with crypto and get file extension like i said from url or with read-chunk and file-type modules.
I'm having trouble issuing a POST command that downloads a file.
On the client side, I'm trying to POST to a specific URL, including a param that specifies the file to download.
var req = $.ajax({
type: 'POST',
url : '/click',
data: { 'path' : filename }
});
req.done(function(data) {
// Download the file here?
The server eventually fires off a method which does this:
function downloadFile(req, res) {
var dir = req.session.currentdir + req.body.path;
mimetype = (shell.exec("file --mime-type '" + dir + "'", {silent:true}).output);
mimetype = mimetype.substring(mimetype.indexOf(": ") + 2, mimetype.length);
var stat = fs.statSync(dir);
res.writeHead(200, {'Content-Type' : mimetype,
'Content-Length': stat.size });
var fileStream = fs.createReadStream(dir);
fileStream.pipe(res);
};
Now I can't seem to get the client side to accept the file I'm trying to pipe back . . it just hangs for an incredibly long time before closing. What is the appropriate way to get the client to download the file I'm trying to send back?
Much thanks for taking the time to read.
1.
resp.setHeader( "Content-Disposition", "attachment; filename=\"xxxx.xxx\"" );
2.
better to use Get