Excel downloaded using window.saveAs() is not opening - javascript

I'm developing and application that has node.js at backend and Angular.js in front end. I'm using exceljs module in node to create a xlsx book. UI is making ajax call to download xlsx file.
Node.js code.
response.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
response.setHeader("Content-Disposition", "attachment; filename=" + quoteId +".xlsx");
workbook.xlsx.write(response).then(function () {
response.end();
});
Angular code
DownloadService.downloadServiceCall(inputParam).then(
function success(res){
var blob = new Blob([res.data], { type: res.headers()['content-type'] });
var header = res.headers()['content-disposition'];
var fileName = header.match(/filename=(.+)/)[1];
window.saveAs(blob,fileName);
});
I'm able to download csv file and open it with above code.
Excel file is getting downloaded but I'm unable to open the same. It throws following error
Anyone have an idea about this issue, please suggest the solution. Any help on this would be appreciated.
Thanks..

It was due to async call from Angular. Has nothing to do with saveAs. Added responseType:'arraybuffer' in the request object. It resolved the issue.
$http({method:'get',url:'',responseType:'arraybuffer'})

Related

File not opening after download: ReactJs + Spring Boot

I am developing a web application and one of my use cases is for users to have the ability to upload and download files from the server. I'm using ReactJs and Spring Boot.
Front-End code:
downloadFileClicked(id, fileName, fileType){
TestDataService.downloadFile(id)
.then(response=>{
console.log(response)
const file = new Blob(
[response.data],
{type: fileType}
)
let url = window.URL.createObjectURL(file)
let a = document.createElement('a')
a.href=url
a.download= fileName
a.click()
})
}
Back-End code:
#GetMapping("/downloadFile/{fileId:.+}")
public ResponseEntity<Resource> downloadFile(#PathVariable Long fileId, HttpServletRequest request ){
//Load file as a resource
DatabaseFile databaseFile = fileStorageService.getFile(fileId);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(databaseFile.getFileType()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+databaseFile.getFileName()+"\"")
.body(new ByteArrayResource(databaseFile.getData()));
}
When the user clicks download - the file does indeed download and seemingly in the correct format but when each application tries to open the file I am faced with such errors as the file format is not supported or the file is corrupt.
The file data is being stored as a byte array in a MySQL database. I have tried a number of different methods to download the file but I have encountered the same error each time. Any help would be greatly appreciated!

How to access data sent by node sever using client side javascript

I am using a node sever to send a table from a sqlite db to the browser. This table contains filename and path of a pdf file that I want to render on the browser. Until now I was using hard coded paths for the the pdf file and rendering. But now i have setup a get route and a controller in node such that whenever '/content' is hit in browser , the server queries the database and and sends the data to the client. To the send the data I am using
res.render('content/index',{data:queryData});
Now, how do I access this data using client side javascript so that I can pass the path of the pdf file to the function that renders the pdf? I have done research and the nearest answer I got was using XMLHttpRequest. I tried this method
var xhr = new XMLHttpRequest();
const path = "http://localhost:3000/content";
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200)
{
var myResponseText = xhr.responseText;
console.log(myResponseText);
}
};
xhr.open('get', path, true);
xhr.send();
When I do this I get the entire html code for the view. Not the data I expected. How do I solve this issue. I have done some more reading while writing this post and I suppose. I have set a header somewhere? But the documentation says
app.render(view, [locals], callback)
which means res.render can take local variables, shouldn't be setting the headers?
You should return json instead of render template:
app.get('content/index', (req, res) => {
res.json({data: queryData});
});
I am using pdf.js
PDF.js needs the PDF file, e.g.:
pdfjsLib.getDocument('helloworld.pdf')
I'm assuming your queryData goes something like this:
{ filename: 'file.pdf', path: './path/to/file.pdf' }
I'm not sure what's in your content/index or what path this is on, but you obviously need to find a way to make your PDF file ('./path/to/file.pdf') available (as a download). See Express's built-in static server or res.download() to do that.
Once you have the PDF file available as a download, plug that path into PDF.js's .getDocument('/content/file.pdf') and do the rest to render the PDF onto the canvas or whatever.
Hope that helps.

Why my ZIP file from NodeJS server is not readable?

I'm making a web service that will return some generated zip files to a client.
Currently I did a really simple code for my test and when I query directly this service the ZIP file is well returned. In a near future, I'll need to pass some parameters through the header and so a simple link on the client side will not do the job.
So I decided to use XmlHttpRequest on my client to query the resource and download it as a simple file. The problem comes here, when I generate the download with the server response, the zip file doesn't work.
Here is the NodeJS code (with express) :
.get('/myservice', function(req, res)
{
res.setHeader('Content-Type', 'application/zip');
const JSZip = require('jszip');
const zip = new JSZip();
zip.file('hello.txt', 'Hello world\n');
zip
.generateAsync({type: 'nodebuffer'})
.then(function(content)
{
res.send(content)
}.bind(res));
}
Here is the client side Javascript that call the service and then make a file from the answer :
var req = new XMLHttpRequest();
req.open("GET", "https://mydomain/myservice", false);
req.send(null);
function download(filename, text)
{
var element = document.createElement('a');
element.setAttribute('href', 'data:application/zip;charset=base64,' + text);
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
console.log(req.responseText)
download('test.zip', req.responseText);
I think that I completely miss understand the encoding on the client, but I didn't find the solution.
The downloaded file, when unzip, give me a file that contain the same zip that also do the same thing.
When I call the service with XmlHttpRequest, it is well called and does not throw any error.
I'm using JSZip to generate the zip.
I precise that I can't use blob on generateAsync from JSZip because that throw me an error.
Any solution ?
You dont need to make it so complicated. As you are piping a file already. Just use window.open('/myservice'); as the browser will handle the fact its a file and keep the current page open and download the file.
https://codesandbox.io/s/hardcore-euclid-9nkft

Is there a way to download any kind of file using angular filesaver?

I'm using node.js and angular.js for my app and I'm trying to download files through the browser using Blob and fileSaver.js.
I've used these in other sections of my app to download text files and pdf files specifying the correct type when creating the Blob object without any problem, but in the current section I need to support any type of file and I don't know if it's possible.
For example, I've tried downloading an image file with and without type:image/png and the result was a corrupted image - inspecting it in a text editor and comparing it with the original file shows that many of the bytes were changed.
Here are the code snippets I use:
Server:
fs.readFile(/* snipped file path */, function(err, data){
if(err){
/* handle error */
}
else{
res.send(data);
}
});
Client:
$http.get(/* endPoint URL */)
.success(function(result){
var data = new Blob([result], {type: 'image/png'});
FileSaver.saveAs(data, filename);
});
A few questions:
Do I need to specify type for Blob? If so, do I need to specify it at server, too (it's a pain to determine it)? Can't I just skip it on both ends?
What causes the image test to result in corrupted file? Am I missing some content-type header or something?
Try adding {contentType: 'arraybuffer'} to your GET request and remove type from Blob definition, like so:
$http.get(/* endPoint URL */, {contentType: 'arraybuffer'})
.success(function(result){
var data = new Blob([result]);
FileSaver.saveAs(data, filename);
});
(Edit: deleted redundant type definition from Blob)

Using ExcelBuilder.js with FileSaver.js

In my application I cant use Downloadify (which is recommended by ExcelBuilder.js) so I tried to download my .xlsx file with FileSaver.js
I tried both
var blob = new Blob([builder.createFile(basicReport.prepare())],{
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base65"
})
saveAs(blob, "myXLSX.xlsx");
and
var blob = new Blob([builder.createFile(basicReport.prepare())],{
type: "application/vnd.ms-excel;charset=charset=utf-8"
})
saveAs(blob, "myXLSX.xlsx");
I can download the file and i tried .xls and .xlsx extensions as well. The Excel cant open the .xlsx and if i try to open the .xls it opens but the data is uninterpretable.
Update 14-Sep-2017: there is a simpler code for this as you can see at https://github.com/eligrey/FileSaver.js/issues/262#issuecomment-256602092. You can remove the new Blob line and just use:
ExcelBuilder.Builder.createFile(workbook, {type:'blob'})
.then(function(blob) {
FileSaver.saveAs(blob, 'File.xlsx');
});
I was having the same issue today, but with the Angular version of FileSaver.js. And I solved it unintentionally. The following code just worked:
ExcelBuilder.Builder.createFile(workbook, {type:'blob'})
.then(function(blob) {
var data = new Blob([blob], {type:'base64'});
FileSaver.saveAs(data, 'File.xlsx');
});
My libraries "angular-file-saver": "1.1.2" and "excel-builder-js": "https://github.com/rodrigosaling/excel-builder.js.git#master" that is a fork of excel-builder.js#2.0.2.
Some clarifications (because it was not 100% unintentional):
the {type:'blob'} came from
https://github.com/stephenliberty/excel-builder.js/issues/4#issuecomment-54961321;
the createFile() returns a promise and not a file (without the then() the library will create a file containing a "Promise" text on the first cell);
my fork of the library is to replace the generate() by generateAsync() on JSZip library. generate() was replaced on v3 that is the version ExcelBuilder requests;
why the {type:'base64'}? It's only a guess, but I think is what an Excel file is after its content is zipped. I don't know.
I couldnt make it with fileSaver.js library so I made a link in my event handler, clicked it and then removed the link.
var myA = document.createElement('a');
myA.setAttribute('href', "'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,' + EB.createFile(workbook));
myA.setAttribute('download', "myXLSX.xlsx");
document.getElementById("mydiv").appendChild(myA);
myA.click();
document.getElementById("mydiv").removeChild(myA);

Categories