How to download exported excel from laravel using axios? - javascript

I have my code below from Backend that is working fine when I test it using Laravel's blade. But I can't do the same in using Axios in my React frontend (see frontend code below).
return (new NewsExport())->download($filename);
I somehow found some solutions from another site: they change the backend code, they use the Storage method to return a link instead of a file. But I don't want to use Storage, I want to prevent overstoring files (in case of a user in the frontend rapidly clicks the download button).
My question how can we download the returned file from the backend in the frontend Axios?
My frontend codes (the codes below successfully download an excel file, but the file is corrupted I think because I can't open it when I test it using Microsoft Excel)
let response = await newsApi.exportNewsList(payload).then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'exc.xlsx'); //or any other extension
document.body.appendChild(link);
link.click();
});
Here is the response.data when I log: screenshot

I think you forgot to specify your responseType as blob in your api options.
const options = {
method: 'POST',
responseType: 'blob', <=== you need this one
data: formData,
url: '/yourApi',
};
return API.request(options);

It might be to do with how you are converting the response to a blob.
Here is a block of code that I always use when I have to do something like this. I have formatted it to suit your code above so hopefully it will work the same way
const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, { type: contentType });
return blob;
};
let response = await newsApi.exportNewsList(payload).then(response => {
const dataBlob = b64toBlob(
response.data,
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64'
);
const link = window.document.createElement('a');
link.href = window.URL.createObjectURL(dataBlob);
link.download = 'exc.xlsx';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});

Related

Blob download not working with JavaScript inside a WebView

I have a React application which needs to support both WebViews and Browsers. I have a functionality where-in we have an API which returns base64String and we need to convert it to PDF and provide a link to user to download the PDF. Below is a sample code snippet.
Code Snippet:
const convertBase64StringToPDF = (data: string) => {
const byteCharacters = atob(data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], { type: 'application/pdf' });
};
getbase64StringData(baseUrl2)
.then((response) => {
const blob = convertBase64StringToPDF(response.foobar);
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'MyDoc' + new Date() + '.pdf';
link.click();
})
The problem I'm facing is when a user clicks on the link we create, the download isn't getting triggered only in WebViews. I searched for solutions but most of them suggest making changes in WebView code. Problem is I don't have control over the App code which has the WebView embedded in it. Is there a way I can solve this in React/JavaScript application? I don't care where the download happens, I just need it to get triggered and get stored in users device.

The web-application in the js-script creates a JSON-object. How can I save it as a file to my hard drive? [duplicate]

I have the following code to let users download data strings in csv file.
exportData = 'data:text/csv;charset=utf-8,';
exportData += 'some csv strings';
encodedUri = encodeURI(exportData);
newWindow = window.open(encodedUri);
It works just fine that if client runs the code it generates blank page and starts downloading the data in csv file.
So I tried to do this with JSON object like
exportData = 'data:text/json;charset=utf-8,';
exportData += escape(JSON.stringify(jsonObject));
encodedUri = encodeURI(exportData);
newWindow = window.open(encodedUri);
But I see only a page with the JSON data displayed on it, not downloading it.
I went through some research and this one claims to work but I don't see any difference to my code.
Am I missing something in my code?
Thanks for reading my question:)
This is how I solved it for my application:
HTML:
<a id="downloadAnchorElem" style="display:none"></a>
JS (pure JS, not jQuery here):
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(storageObj));
var dlAnchorElem = document.getElementById('downloadAnchorElem');
dlAnchorElem.setAttribute("href", dataStr );
dlAnchorElem.setAttribute("download", "scene.json");
dlAnchorElem.click();
In this case, storageObj is the js object you want to store, and "scene.json" is just an example name for the resulting file.
This approach has the following advantages over other proposed ones:
No HTML element needs to be clicked
Result will be named as you want it
no jQuery needed
I needed this behavior without explicit clicking since I want to trigger the download automatically at some point from js.
JS solution (no HTML required):
function downloadObjectAsJson(exportObj, exportName){
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
var downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", exportName + ".json");
document.body.appendChild(downloadAnchorNode); // required for firefox
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
Found an answer.
var obj = {a: 123, b: "4 5 6"};
var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj));
$('download JSON').appendTo('#container');
seems to work fine for me.
** All credit goes to #cowboy-ben-alman, who is the author of the code above **
You could try using:
the native JavaScript API's Blob constructor and
the FileSaver.js saveAs() method
No need to deal with any HTML elements at all.
var data = {
key: 'value'
};
var fileName = 'myData.json';
// Create a blob of the data
var fileToSave = new Blob([JSON.stringify(data)], {
type: 'application/json'
});
// Save the file
saveAs(fileToSave, fileName);
If you wanted to pretty print the JSON, per this answer, you could use:
JSON.stringify(data,undefined,2)
This would be a pure JS version (adapted from cowboy's):
var obj = {a: 123, b: "4 5 6"};
var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj));
var a = document.createElement('a');
a.href = 'data:' + data;
a.download = 'data.json';
a.innerHTML = 'download JSON';
var container = document.getElementById('container');
container.appendChild(a);
http://jsfiddle.net/sz76c083/1
ES6+ version for 2021; no 1MB limit either:
This is adapted from #maia's version, updated for modern Javascript with the deprecated initMouseEvent replaced by new MouseEvent() and the code generally improved:
const saveTemplateAsFile = (filename, dataObjToWrite) => {
const blob = new Blob([JSON.stringify(dataObjToWrite)], { type: "text/json" });
const link = document.createElement("a");
link.download = filename;
link.href = window.URL.createObjectURL(blob);
link.dataset.downloadurl = ["text/json", link.download, link.href].join(":");
const evt = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true,
});
link.dispatchEvent(evt);
link.remove()
};
If you want to pass an object in:
saveTemplateAsFile("filename.json", myDataObj);
Simple, clean solution for those who only target modern browsers:
function downloadTextFile(text, name) {
const a = document.createElement('a');
const type = name.split(".").pop();
a.href = URL.createObjectURL( new Blob([text], { type:`text/${type === "txt" ? "plain" : type}` }) );
a.download = name;
a.click();
}
downloadTextFile(JSON.stringify(myObj), 'myObj.json');
The following worked for me:
/* function to save JSON to file from browser
* adapted from http://bgrins.github.io/devtools-snippets/#console-save
* #param {Object} data -- json object to save
* #param {String} file -- file name to save to
*/
function saveJSON(data, filename){
if(!data) {
console.error('No data')
return;
}
if(!filename) filename = 'console.json'
if(typeof data === "object"){
data = JSON.stringify(data, undefined, 4)
}
var blob = new Blob([data], {type: 'text/json'}),
e = document.createEvent('MouseEvents'),
a = document.createElement('a')
a.download = filename
a.href = window.URL.createObjectURL(blob)
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
a.dispatchEvent(e)
}
and then to call it like so
saveJSON(myJsonObject, "saved_data.json");
I recently had to create a button that would download a json file of all values of a large form. I needed this to work with IE/Edge/Chrome. This is what I did:
function download(text, name, type)
{
var file = new Blob([text], {type: type});
var isIE = /*#cc_on!#*/false || !!document.documentMode;
if (isIE)
{
window.navigator.msSaveOrOpenBlob(file, name);
}
else
{
var a = document.createElement('a');
a.href = URL.createObjectURL(file);
a.download = name;
a.click();
}
}
download(jsonData, 'Form_Data_.json','application/json');
There was one issue with filename and extension in edge but at the time of writing this seemed to be a bug with Edge that is due to be fixed.
Hope this helps someone
downloadJsonFile(data, filename: string){
// Creating a blob object from non-blob data using the Blob constructor
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
// Create a new anchor element
const a = document.createElement('a');
a.href = url;
a.download = filename || 'download';
a.click();
a.remove();
}
You can easily auto download file with using Blob and transfer it in first param downloadJsonFile. filename is name of file you wanna set.
The download property of links is new and not is supported in Internet Explorer (see the compatibility table here). For a cross-browser solution to this problem I would take a look at FileSaver.js
If you prefer console snippet, raser, than filename, you can do this:
window.open(URL.createObjectURL(
new Blob([JSON.stringify(JSON)], {
type: 'application/binary'}
)
))
React: add this where you want in your render method.
• Object in state:
<a
className="pull-right btn btn-primary"
style={{ margin: 10 }}
href={`data:text/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(this.state.objectToDownload)
)}`}
download="data.json"
>
DOWNLOAD DATA AS JSON
</a>
• Object in props:
<a
className="pull-right btn btn-primary"
style={{ margin: 10 }}
href={`data:text/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(this.props.objectToDownload)
)}`}
download="data.json"
>
DOWNLOAD DATA AS JSON
</a>
className and style are optional, modify the style according to your needs.
Try to set another MIME-type:
exportData = 'data:application/octet-stream;charset=utf-8,';
But there are can be problems with file name in save dialog.
const exportToJson = (data: {}) =>{
const link = document.createElement("a");
link.href =
data:text/json;charset=utf8,${encodeURIComponent(JSON.stringify(data))}; link.download = 'example.json'; link.click(); }
Make sure to clean up the the created link after if you don't want a random element that does nothing.

How to create a PDF on the fly with javascript and the PDF data?

I have an AJAX call which calls a secure PHP API call that generates PDF file data (on the fly, no actual file) and returns it. How can I use this PDF file data in javascript/jquery to create and download a PDF file to the user?
I've originally tried setting the ajax call's heads to that of a PDF, but obviously you can't download files via ajax.
To automatically download a file from ajax call you can do the following. In my example I returned an object that contained the file name, mime type, and Base64 data but maybe this can give you an idea on how to do what you are looking for.
PostDTO('/ReportService.asmx/DownloadReport', dto, function (result) {
var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
// Do it the HTML5 compliant way
var blob = base64ToBlob(result.FileDataBase64, result.MimeType);
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = result.Filename;
a.click();
window.URL.revokeObjectURL(url);
}
});
function base64ToBlob(base64, mimetype, slicesize) {
if (!window.atob || !window.Uint8Array) {
// The current browser doesn't have the atob function. Cannot continue
return null;
}
mimetype = mimetype || '';
slicesize = slicesize || 512;
var bytechars = atob(base64);
var bytearrays = [];
for (var offset = 0; offset < bytechars.length; offset += slicesize) {
var slice = bytechars.slice(offset, offset + slicesize);
var bytenums = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
bytenums[i] = slice.charCodeAt(i);
}
var bytearray = new Uint8Array(bytenums);
bytearrays[bytearrays.length] = bytearray;
}
return new Blob(bytearrays, { type: mimetype });
};
You can download file from ajax.
I did it from an angular application.
The idea :
Get the file data and ask a response type "arrayBuffer"
Be sure the data response are not transformed by your framework (as it is with angular)
Create a blob : var blob = new Blob([data], {type: 'application/pdf');//check type
Create an url : var url = URL.createObjectURL(blob);
Open the url : window.open(url);
You can verify the blob generated via the console, the blob url is showed. That url must open your pdf.

Client download of a server generated zip file

Before somebody says, "duplicate", I just want to make sure, that folks know, that I have already reviewed these questions:
1) Uses angular and php, not sure what is happening here (I don't know PHP): Download zip file and trigger "save file" dialog from angular method
2) Can't get this answer to do anything: how to download a zip file using angular
3) This person can already download, which is past the point I'm trying to figure out:
Download external zip file from angular triggered on a button action
4) No answer for this one:
download .zip file from server in nodejs
5) I don't know what language this even is:
https://stackoverflow.com/questions/35596764/zip-file-download-using-angularjs-directive
Given those questions, if this is still a duplicate, I apologize. Here is, yet, another version of this question.
My angular 1.5.X client gives me a list of titles, of which each have an associated file. My Node 4.X/Express 4.X server takes that list, gets the file locations, creates a zip file, using express-zip from npm, and then streams that file back in the response. I then want my client to initiate the browser's "download a file" option.
Here's my client code (Angular 1.5.X):
function bulkdownload(titles){
titles = titles || [];
if ( titles.length > 0 ) {
$http.get('/query/bulkdownload',{
params:{titles:titles},
responseType:'arraybuffer'
})
.then(successCb,errorCb)
.catch(exceptionCb);
}
function successCb(response){
// This is the part I believe I cannot get to work, my code snippet is below
};
function errorCb(error){
alert('Error: ' + JSON.stringify(error));
};
function exceptionCb(ex){
alert('Exception: ' + JSON.stringify(ex));
};
};
Node (4.X) code with express-zip, https://www.npmjs.com/package/express-zip:
router.get('/bulkdownload',function(req,resp){
var titles = req.query.titles || [];
if ( titles.length > 0 ){
utils.getFileLocations(titles).
then(function(files){
let filename = 'zipfile.zip';
// .zip sets Content-Type and Content-disposition
resp.zip(files,filename,console.log);
},
_errorCb)
}
});
Here's my successCb in my client code (Angular 1.5.X):
function successCb(response){
var URL = $window.URL || $window.webkitURL || $window.mozURL || $window.msURL;
if ( URL ) {
var blob = new Blob([response.data],{type:'application/zip'});
var url = URL.createObjectURL(blob);
$window.open(url);
}
};
The "blob" part seems to work fine. Checking it in IE's debugger, it does look like a file stream of octet information. Now, I believe I need to get that blob into the some HTML5 directive, to initiate the "Save File As" from the browser. Maybe? Maybe not?
Since 90%+ of our users are using IE11, I test all of my angular in PhantomJS (Karma) and IE. When I run the code, I get the old "Access is denied" error in an alert window:
Exception: {"description":"Access is denied...<stack trace>}
Suggestions, clarifications, answers, etc. are welcome!
Use this one:
var url="YOUR ZIP URL HERE";
window.open(url, '_blank');
var zip_file_path = "" //put inside "" your path with file.zip
var zip_file_name = "" //put inside "" file name or something
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = zip_file_path;
a.download = zip_file_name;
a.click();
document.body.removeChild(a);
As indicated in this answer, I have used the below Javascript function and now I am able to download the byte[] array content successfully.
Function to convert byte array stream (type of string) to blob object:
var b64toBlob = function(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
};
An this is how I call this function and save the blob object with FileSaver.js (getting data via Angular.js $http.get):
$http.get("your/api/uri").success(function(data, status, headers, config) {
//Here, data is type of string
var blob = b64toBlob(data, 'application/zip');
var fileName = "download.zip";
saveAs(blob, fileName);
});
Note: I am sending the byte[] array (Java-Server-Side) like this:
byte[] myByteArray = /*generate your zip file and convert into byte array*/ new byte[]();
return new ResponseEntity<byte[]>(myByteArray , headers, HttpStatus.OK);
I updated my bulkdownload method to use $window.open(...) instead of $http.get(...):
function bulkdownload(titles){
titles = titles || [];
if ( titles.length > 0 ) {
var url = '/query/bulkdownload?';
var len = titles.length;
for ( var ii = 0; ii < len; ii++ ) {
url = url + 'titles=' + titles[ii];
if ( ii < len-1 ) {
url = url + '&';
}
}
$window.open(url);
}
};
I have only tested this in IE11.

PDF is blank when downloading using javascript

I have a web service that returns PDF file content in its response. I want to download this as a pdf file when user clicks the link. The javascript code that I have written in UI is as follows:
$http.get('http://MyPdfFileAPIstreamURl').then(function(response){
var blob=new File([response],'myBill.pdf',{type: "text/pdf"});
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myBill.pdf";
link.click();
});
'response' contains the PDF byte array from servlet outputstream of 'MyPdfFileAPIstreamURl'. And also the stream is not encrypted.
So when I click the link, a PDF file gets downloaded successfully of size around 200KB. But when I open this file, it opens up with blank pages. The starting content of the downloaded pdf file is in the image.
I can't understand what is wrong here. Help !
This is the downloaded pdf file starting contents:
solved it via XMLHttpRequest and xhr.responseType = 'arraybuffer';
code:
var xhr = new XMLHttpRequest();
xhr.open('GET', './api/exportdoc/report_'+id, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) {
var blob=new Blob([this.response], {type:"application/pdf"});
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="Report_"+new Date()+".pdf";
link.click();
}
};
xhr.send();
i fetched the data from server as string(which is base64 encoded to string) and then on client side i decoded it to base64 and then to array buffer.
Sample code
function solution1(base64Data) {
var arrBuffer = base64ToArrayBuffer(base64Data);
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
var newBlob = new Blob([arrBuffer], { type: "application/pdf" });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
var data = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
document.body.appendChild(link); //required in FF, optional for Chrome
link.href = data;
link.download = "file.pdf";
link.click();
window.URL.revokeObjectURL(data);
link.remove();
}
function base64ToArrayBuffer(data) {
var binaryString = window.atob(data);
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;
};
I was facing the same problem in my React project.
On the API I was using res.download() of express to attach the PDF file in the response. By doing that, I was receiving a string based file. That was the real reason why the file was opening blank or corrupted.
In my case the solution was to force the responseType to 'blob'. Since I was making the request via axios, I just simply added this attr in the option object:
axios.get('your_api_url_here', { responseType: 'blob' })
After, to make the download happen, you can do something like this in your 'fetchFile' method:
const response = await youtServiceHere.fetchFile(id)
const pdfBlob = new Blob([response.data], { type: "application/pdf" })
const blobUrl = window.URL.createObjectURL(pdfBlob)
const link = document.createElement('a')
link.href = blobUrl
link.setAttribute('download', customNameIfYouWantHere)
link.click();
link.remove();
URL.revokeObjectURL(blobUrl);
solved it thanks to rom5jp but adding the sample code for golang and nextjs
in golang using with gingonic context
c.Header("Content-Description", "File-Transfer")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Content-Disposition","attachment; filename="+fileName)
c.Header("Content-Type", "application/pdf; charset=utf-8")
c.File(targetPath)
//c.FileAttachment(targetPath,fileName)
os.RemoveAll(targetPath)
in next js
const convertToPDF = (res) => {
const uuid = generateUUID();
var a = document.createElement('a');
var url = window.URL.createObjectURL(new Blob([res],{type: "application/pdf"}));
a.href = url;
a.download = 'report.pdf';
a.click();
window.URL.revokeObjectURL(url);
}
const convertFile = async() => {
axios.post('http://localhost:80/fileconverter/upload', {
"token_id" : cookies.access_token,
"request_type" : 1,
"url" : url
},{
responseType: 'blob'
}).then((res)=>{
convertToPDF(res.data)
}, (err) => {
console.log(err)
})
}
I was able to get this working with fetch using response.blob()
fetch(url)
.then((response) => response.blob())
.then((response) => {
const blob = new Blob([response], {type: 'application/pdf'});
const link = document.createElement('a');
link.download = 'some.pdf';
link.href = URL.createObjectURL(blob);
link.click();
});
Changing the request from POST to GET fixed it for me

Categories

Resources