AngularJS $http-post - convert binary to excel file and download
Solution 1
Just noticed you can't use it because of IE8/9 but I'll push submit anyway... maybe someone finds it useful
This can actually be done through the browser, using blob
. Notice the responseType
and the code in the success
promise.
$http({
url: 'your/webservice',
method: "POST",
data: json, //this is your json data string
headers: {
'Content-type': 'application/json'
},
responseType: 'arraybuffer'
}).success(function (data, status, headers, config) {
var blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
}).error(function (data, status, headers, config) {
//upload failed
});
There are some problems with it though like:
- It doesn't support IE 8 and 9:
- It opens a pop up window to open the
objectUrl
which people might have blocked - Generates weird filenames
It did work!
The server side code in PHP I tested this with looks like this. I'm sure you can set similar headers in Java:
$file = "file.xlsx";
header('Content-disposition: attachment; filename='.$file);
header('Content-Length: ' . filesize($file));
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
header('Pragma: public');
echo json_encode(readfile($file));
Edit 20.04.2016
Browsers are making it harder to save data this way. One good option is to use filesaver.js. It provides a cross browser implementation for saveAs
, and it should replace some of the code in the success
promise above.
Solution 2
This is how you do it:
- Forget IE8/IE9, it is not worth the effort and does not pay the money back.
- You need to use the right HTTP header,use Accept to 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' and also you need to put responseType to 'arraybuffer'(ArrayBuffer but set with lowercase).
- HTML5 saveAs is used to save the actual data to your wanted format. Note it will still work without adding type in this case.
$http({ url: 'your/webservice', method: 'POST', responseType: 'arraybuffer', data: json, //this is your json data string headers: { 'Content-type': 'application/json', 'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } }).success(function(data){ var blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); saveAs(blob, 'File_Name_With_Some_Unique_Id_Time' + '.xlsx'); }).error(function(){ //Some error log });
Tip! Don't mix " and ', stick to always use ', in a professional environment you will have to pass js validation for example jshint, same goes for using === and not ==, and so on, but that is another topic :)
I would put the save excel in another service, so you have clean structure and the post is in a proper service of its own. I can make a JS fiddle for you, if you don't get my example working. Then I would also need some json data from you that you use for a full example.
Happy coding.. Eduardo
Solution 3
Download the server response as an array buffer. Store it as a Blob using the content type from the server (which should be application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
):
var httpPromise = this.$http.post(server, postData, { responseType: 'arraybuffer' });
httpPromise.then(response => this.save(new Blob([response.data],
{ type: response.headers('Content-Type') }), fileName));
Save the blob to the user's device:
save(blob, fileName) {
if (window.navigator.msSaveOrOpenBlob) { // For IE:
navigator.msSaveBlob(blob, fileName);
} else { // For other browsers:
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
window.URL.revokeObjectURL(link.href);
}
}
Solution 4
Worked for me -
$scope.downloadFile = function () {
Resource.downloadFile().then(function (response) {
var blob = new Blob([response.data], { type: "application/pdf" });
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
},
function (error) {
debugger;
});
};
Which calls the following from my resource factory-
downloadFile: function () {
var downloadRequst = {
method: 'GET',
url: 'http://localhost/api/downloadFile?fileId=dfckn4niudsifdh.pdf',
headers: {
'Content-Type': "application/pdf",
'Accept': "application/pdf"
},
responseType: 'arraybuffer'
}
return $http(downloadRequst);
}
Make sure your API sets the header content type too -
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
Solution 5
There is no way (to my knowledge) to trigger the download window in your browser from Javascript. The only way to do it is to redirect the browser to a url that streams the file to the browser.
If you can modify your REST service, you might be able to solve it by changing so the POST request doesn't respond with the binary file, but with a url to that file. That'll get you the url in Javascript instead of the binary data, and you can redirect the browser to that url, which should prompt the download without leaving the original page.
Alex Man
profile for user123 at Stack Overflow, Q&A for professional and enthusiast programmers http://stackoverflow.com/users/flair/3253853.png
Updated on July 09, 2022Comments
-
Alex Man almost 2 years
I've created an application in Angular JS for downloading an Excel workbook through $http post.
In the below code I'm passing the information in the form of JSON , and send it to the server REST web service (java) through an angular $http post. The web service uses the information from the JSON and produces an Excel workbook. In the response within the success body of $http post, I'm getting binary data within that data variable, but don't know how to convert it and download as an Excel file.
Can anyone please tell me some solution for this for converting the binary to Excel file and download?
My code is as given below:
$http({ url: 'myweb.com/myrestService', method: "POST", data: json, //this is your json data string headers: { 'Content-type': 'application/json' } }).success(function (data, status, headers, config) { // Here i'm getting excel sheet binary datas in 'data' }).error(function (data, status, headers, config) { });
-
user2171669 over 9 yearsI think in many cases it wont work. U need to set content type of $http to 'blob'.
-
Simon H about 9 yearsWorked from me.. but depends on github.com/eligrey/FileSaver.js/blob/master/FileSaver.min.js FileSaver for saveAs
-
nosdalg almost 9 years@jorg Is it possible to add similar headers in django python.
-
Jorg almost 9 years@Explore-X yes I'm positive you can. Not familiar with django though, so I can't tell you how exactly.
-
Mitaksh Gupta almost 9 years@Jorg in the screenshot of the Excel you have shared above, the text in the field, i.e. "my content", it is exceeding the width of the cell, is there an option through which I can set the options like default column-width or word wrap? The issue I am facing is that while printing a file like your above, the data gets truncated if there are multiple columns and the width of the data is more than the width of the column. Much thanks!
-
Jorg almost 9 years@MitakshGupta the file is a copy of what you saved. If you generated it with the proper column width it would come through that way I imagine. I can try if you like.
-
Mitaksh Gupta almost 9 years@Jorg Please have a look at this question I posted a couple of days back stackoverflow.com/questions/31918248/… This gives the exact description of the problem I am facing
-
chipit24 over 8 yearsThis worked for me as well. Take a look at github.com/alferov/angular-file-saver if you're using AngularJS. And if you won't be supported IE8/9, it may be nice to let the user know, for that I use this: github.com/burocratik/outdated-browser. Also, setting
responseType: 'arraybuffer'
is important. -
treejanitor over 8 yearsExcellent! The
responseType: 'arraybuffer'
was the key for me. I had to fight with connect-livereload issues causing zip file corruption on the server, so this angular tweak was much faster-found welcome fix! Thanks! -
Jorg about 8 yearsTry filesaver.js instead of window.open
-
puiu about 7 yearsJust going to piggyback on the most upvoted comment to add that you don't need to use a
responsetype: arraybuffer
because you then have to manually convert it to a blob usingnew Blob()
. Unless you need to modify the incoming blob from the server, then just use a response type of blob directly. -
Vinoth about 7 yearscan you answer this question stackoverflow.com/questions/43894768/…
-
Vinoth about 7 yearscan you answer this question stackoverflow.com/questions/43894768/…
-
Wella almost 7 yearsgetting this error in safari. "'[object BlobConstructor]' is not a constructor (evaluating 'new Blob([data], { type: contentType })')"
-
Sledge over 6 yearsIs there a good way to control the filename using this approach?
-
Hardik Sondagar about 6 yearsNo longer works after Chrome 65 update. It's blocking cross-origin <a download> chromestatus.com/feature/4969697975992320
-
Jorg about 6 years@HardikSondagar FileSaver.js is working on this: github.com/eligrey/FileSaver.js/issues/428
-
Hardik Sondagar about 6 years@Jorg Can we create a dynamic form with blob URL as an action and submit using
js
? -
dierre almost 5 yearsis there a way to get the filename in the header with this solution?
-
dierre almost 5 yearsis there a way to get the filename from the headers?