Download File from Bytes in JavaScript - javascript

I want to download the file which is coming in the form of bytes from the AJAX response.
I tried to do it this way with the help of Blob:
var blob=new Blob([resultByte], {type: "application/pdf"});
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myFileName.pdf";
link.click();
It is in fact downloading the pdf file but the file itself is corrupted.
How can I accomplish this?

I asked the question long time ago, so I might be wrong in some details.
It turns out that Blob needs array buffers. That's why base64 bytes need to be converted to array buffers first.
Here is the function to do that:
function base64ToArrayBuffer(base64) {
var binaryString = window.atob(base64);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes;
}
Here is my function to save a pdf file:
function saveByteArray(reportName, byte) {
var blob = new Blob([byte], {type: "application/pdf"});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
var fileName = reportName;
link.download = fileName;
link.click();
};
Here is how to use these two functions together:
var sampleArr = base64ToArrayBuffer(data);
saveByteArray("Sample Report", sampleArr);

You just need to add one extra line and it should work. Your response is byte array from your server application
var bytes = new Uint8Array(resultByte); // pass your byte response to this constructor
var blob=new Blob([bytes], {type: "application/pdf"});// change resultByte to bytes
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myFileName.pdf";
link.click();

Set Blob type at Blob constructor instead of at createObjectURL
var blob = new Blob([resultByte], {type: "application/pdf"});
var link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = "myFileName.pdf";
link.click();

Easiest way would be converting bytes to the base64 format and construct link as below
let link=document.createElement('a');
const mimeType = "application/pdf";
link.href=`data:${mimeType};base64,${base64Str}`;
link.download="myFileName.pdf";
link.click();
Link can be generated on backend side and retrieved from the response.
File bytes can be read as base64 string in Python as following:
with open("my-file.pdf", "rb") as file:
base46_str = base64.b64encode(file.read()).decode("utf-8")

Related

how to get mime type from content-type

The thing is axios calls return files. sometimes xlsx, sometimes plain txt.
In javascript, as soon as I get them, i force download it via blob.
Something like this:
var headers = response.headers;
var blob = new Blob([response.data], {
type: headers['content-type']
});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "report.xlsx";
link.click();
As you see, I got something like this: link.download = "report.xlsx" . What I want is to replace xlsx with dynamic mime type so that sometimes it's report.txt and sometimes it's report.xlsx.
How do I do that from content-type?
You can get the file extension using the content type of headers.
Use this Javascript library - node-mime
You just want to pass your headers['content-type'], it will give you the file extension which you need to set for download name.
var ctype = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
console.log(mime.getExtension(ctype));
<script src="https://wzrd.in/standalone/mime#latest"></script>
Example: In your case,
var headers = response.headers;
var blob = new Blob([response.data], {
type: headers['content-type']
});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "report." + mime.getExtension(headers['content-type']);
link.click();
Incomplete list of MIME types from Mozilla Developers.
What is the backend of your application? I used this in C# (.NET Core) to get the content type of a file then set it as a header in the response:
public string GetContentType (string filePath) {
var contentTypeProvider = new FileExtensionContentTypeProvider();
string contentType;
if( !contentTypeProvider.TryGetContentType( filePath, out contentType ) ) {
contentType = "application/octet-stream";
};
return contentType;
}
Edit: modified OP code to handle content type dynamically:
var headers = response.headers;
var responseType = headers['content-type'];
var fileType = "text/plain";
var fileName = "report.txt";
if ( responseType == "application/octet-stream" ) {
fileType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
fileName = "report.xlsx";
}
var blob = new Blob([response.data], {
type: fileType
});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();

Downloaded PDF looks empty although it contains some data

I'm trying to implement a PDF file download functionality with JavaScript.
As a response to a POST request I get a PDF file, in Chrome DevTools console it looks like (the oResult data container, fragment):
"%PDF-1.4↵%����↵4 0 obj↵<</Filter/FlateDecode/Length 986>>stream↵x��
Now I'm trying to initialize the download process:
let blob = new Blob([oResult], {type: "application/pdf"});
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "tstPDF";
link.click();
As a result, upon a click on a button I get tstPDF.pdf, it contains the correct number of pages, but the PDF itself is empty, no content is displayed, although it is 6 KB.
When I test the Java server-side module, which generates the PDF, everything is working fine, it sends InputStream through ServletOutputStream. Thus I assume that the issue is somewhere on a client side, perhaps something with MIME, BLOB, encoding, or similar.
Why doesn't the generated PDF display any data?
I solved the issue.
The problem was in a way the data is delivered from the server to the client.
It is critical to assure that the server sends the data in Base64 encoding, otherwise the client side can't deserialize the PDF string back to the binary format. Below, you can find the full solution.
Server-side:
OutputStream pdfStream = PDFGenerator.pdfGenerate(data);
String pdfFileName = "test_pdf";
// represent PDF as byteArray for further serialization
byte[] byteArray = ((java.io.ByteArrayOutputStream) pdfStream).toByteArray();
// serialize PDF to Base64
byte[] encodedBytes = java.util.Base64.getEncoder().encode(byteArray);
response.reset();
response.addHeader("Pragma", "public");
response.addHeader("Cache-Control", "max-age=0");
response.setHeader("Content-disposition", "attachment;filename=" + pdfFileName);
response.setContentType("application/pdf");
// avoid "byte shaving" by specifying precise length of transferred data
response.setContentLength(encodedBytes.length);
// send to output stream
ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(encodedBytes);
servletOutputStream.flush();
servletOutputStream.close();
Client side:
let binaryString = window.atob(data);
let binaryLen = binaryString.length;
let bytes = new Uint8Array(binaryLen);
for (let i = 0; i < binaryLen; i++) {
let ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
let blob = new Blob([bytes], {type: "application/pdf"});
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = pdfFileName;
link.click();
Reference topics:
How to convert a PDF generating in response.outputStream to a Base64 encoding
Download File from Bytes in JavaScript
Thanks to this. It really works.
BTW, here's how I do it using spring controller and ajax with pdf generated by jasper
The Controller:
public ResponseEntity<?> printPreview(#ModelAttribute("claim") Claim claim)
{
try
{
//Code to get the byte[] from jasper report.
ReportSource source = new ReportSource(claim);
byte[] report = reportingService.exportToByteArrayOutputStream(source);
//Conversion of bytes to Base64
byte[] encodedBytes = java.util.Base64.getEncoder().encode(report);
//Setting Headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
headers.setContentDispositionFormData("pdfFileName.pdf", "pdfFileName.pdf");
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
headers.setContentLength(encodedBytes.length);
return new ResponseEntity<>(encodedBytes, headers, HttpStatus.OK);
}
catch (Exception e)
{
LOG.error("Error on generating report", e);
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
The ajax:
$.ajax({
type: "POST",
url: "",
data: form.serialize(), //Data from my form
success: function(response)
{
let binaryString = window.atob(response);
let binaryLen = binaryString.length;
let bytes = new Uint8Array(binaryLen);
for (let i = 0; i < binaryLen; i++) {
let ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
let blob = new Blob([bytes], {type: "application/pdf"});
let link = URL.createObjectURL(blob);
window.open(link, '_blank');
},
error: function()
{
}
});
This will load the pdf in new window.
References: Return generated pdf using spring MVC

Is it possible to convert byte array/stream to files such as Word,Excel, PDF in JavaScript

I have a query regarding file operations using JavaScript-
Scenario - My JS function calls a wcf service which returns the file content in the form of byte array or stream and the mime type. This byte array/stream needs to be converted to a file and which will be downloaded on user's machine.
Reference code -
var arr = "This is test content";
var byteArray = new Uint8Array(arr);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob([byteArray], {
type: 'text/plain'
}));
a.download = "Test";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
The code works for only text files. Files with mime type other than text are corrupted.
I understand that file operations are severly restricted at client side, but just to confirm - Is there anyway to convert byte array/stream to files such as Word,Excel, PDF and etc ?
I accomplish a similar goal with this. Pick up from where you have your byteArray, and try this:
var byteArray = new Uint8Array(arrayBuffer);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob([byteArray], { type:'application/octet-stream' }));
// supply your own fileName here...
a.download = "YourFileName.XLSX";
document.body.appendChild(a)
a.click();
document.body.removeChild(a)
Setting contentType to "application/octet-stream" will accommodate any binary file type.

Javascript: Exporting large text/csv file crashes Google Chrome

I have the following Javascript code to export CSV file on the client side. However Google Chrome crashes every time I try to export a large array. What is the limit of the data string allowed in Chrome? Is it possible that it is hitting the memory limit allowed in Chrome? If the data string is too long for Chrome, how will I go about exporting large CSV files on the client side?
var csvRows = [...]; //Array with 40000 items, each item is 100 characters long.
var csvString = csvRows.join("\r\n");
var a = document.createElement('a');
a.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csvString);
a.target = '_blank';
a.download = 'export.csv';
document.body.appendChild(a);
a.click();
(Expected file size is about 6.4MB)
had the same Problem and solved it using Blob.
For example:
csvData = new Blob([csvString], { type: 'text/csv' });
var csvUrl = URL.createObjectURL(csvData);
a.href = csvUrl;
Source:
https://stackoverflow.com/a/24611096/3048937
I used following function to download CSV. Worked for me in IE/Firefox/Chrome
function downloadFile(data, fileName) {
var csvData = data;
var blob = new Blob([ csvData ], {
type : "application/csv;charset=utf-8;"
});
if (window.navigator.msSaveBlob) {
// FOR IE BROWSER
navigator.msSaveBlob(blob, fileName);
} else {
// FOR OTHER BROWSERS
var link = document.createElement("a");
var csvUrl = URL.createObjectURL(blob);
link.href = csvUrl;
link.style = "visibility:hidden";
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}

Save json string to client pc (using HTML5 API)

I read few older thread about the same, but seen the file API changed a lot recently. My requirement is to save a json file (data is locally in indexdDB, but I need a way to back it up). Since I use indexdDB, I only target recent browsers, mainly chrome. So, it it possible to save data (json string) to client computer?
I have seen http://eligrey.com/demos/FileSaver.js/ , but is there a way to do it natively?
Thanks.
You can use a Blob and the HTML5 a[download] feature to provide a JSON backup download:
var data = {a:1, b:2, c:3};
var json = JSON.stringify(data);
var blob = new Blob([json], {type: "application/json"});
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.download = "backup.json";
a.href = url;
a.textContent = "Download backup.json";
Here is a jsfiddle example: http://jsfiddle.net/potatosalad/yuM2N/
Yes, you can. This assumes that you have the json in text:
var toDownload=new Blob([text],{type:'x-whatever/x-backup'});
var link=window.URL.createObjectURL(toDownload);
window.location=link;
that is untested, but it should work.
You can use FileSaver.js.
Sample code:
//include the js file in html.
<script src="FileSaver.min.js"></script>
// other code ...
//use it here.
var myjson= "{a:3, b:4}";
var blob = new Blob([myjson], {type: "application/json"});
var saveAs = window.saveAs;
saveAs(blob, "my_outfile.json");
Use JSON.stringify to create a string from JSON.
Fiddle: https://jsfiddle.net/9w9ofec4/3/
based on potatosalad answer i experimented with an 'self' updating link:
jsfiddle
function saveAsFile(link, content, filename) {
var blob = new Blob([content], {type: "text/text"});
var url = URL.createObjectURL(blob);
// update link to new 'url'
link.download = filename + ".txt";
link.href = url;
}
saveAsFile(this, "YourContent", "HelloWorldFile");
the function saveAsFile() needs the calling a element as first argument.
than it updates the href target to the new blob.
function saveAsJSON(data, name=Date.now()+'.json') {
const a = document.createElement('a')
a.download = name
a.href = URL.createObjectURL(new Blob([JSON.stringify(data)], {type: 'application/json'}))
a.click()
}
// https://stackoverflow.com/questions/62371219/chrome-stops-download-files-from-stackoverflow-snippets
saveAsJSON(['orange', 'banana', {name: 'apple'}])
To save the file with a custom name, you can create a hidden <a> element and then click on it. This method is used by FileSaver.js.
function download(name, text){
var toDownload=new Blob([text],
{type:'data:application/octet-stream'});
var link = window.URL.createObjectURL(toDownload);
var el = document.createElement("a");
el.href = link;
el.download = name;
el.click();
window.URL.revokeObjectURL(link);
}

Categories