In my Ext Js solution I am calling a service which is returning this JSON format
{"success":true,"filename":"spreadsheet.xlsx","file":[80,75,3,4,20,0,...(many more)]}
How can I make a file download dialog with the filename and the content of the byte array (file) ?
UPDATE
So I found this bit to start the downlaod
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob(data.file, { type: 'application/octet-stream' }));
a.download = data.filename;
// Append anchor to body.
document.body.appendChild(a)
a.click();
// Remove anchor from body
document.body.removeChild(a)
So far good
But the file I get is corrupted so I suspect I need to Encode/Decode the file variable?
I had to convert the file into a Uint8Array before passing it to the Blob
var arr = data.file;
var byteArray = new Uint8Array(arr);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob([byteArray], { type: 'application/octet-stream' }));
a.download = data.filename;
// Append anchor to body.
document.body.appendChild(a)
a.click();
// Remove anchor from body
document.body.removeChild(a)
Reading this answer helped a lot https://stackoverflow.com/a/16245768/1016439
Building on Jepzen's response, I was able to use this technique to download a document from AWS S3 from within the browser. +1 Jepzen
s3.getObject(params, function(err, data) {
if (err === null) {
var arr = data.Body;
var byteArray = new Uint8Array(arr);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob([byteArray], { type: 'application/octet-stream' }));
a.download = fName; //fName was the file name portion of the key what was passed in as part of the key value within params.
// Append anchor to body.
document.body.appendChild(a)
a.click();
// Remove anchor from body
document.body.removeChild(a)
} else {
result = 'failure'
console.log("Failed to retrieve an object: " + err);
}
});
Related
I got Node JS server which gets XLSX file contents from metabase:
app.get('/channels', async (req, res) => {
// make request to metabase and take response as XLSX
const queryRequestURL = `${api}/public/card/${cardId}/query/xlsx?parameters=${params}`;
const result = got(queryRequestURL);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader("Content-Disposition", "attachment; filename=file.xlsx");
return res.send(res);
});
It returns file contents like
So when i make request to server and receive response - it comes as file contents above.
I need to download this data as ordinary excel file on browser side.
What i've tried:
// make request with typical fetch and get result to res variable.
const filename = 'file.xlsx';
const file = new File(res, filename ,{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
// create link and click it virtually to download created file
var a = document.createElement('a');
a.href = window.URL.createObjectURL(file);
a.download = filename;
a.click();
But I'm getting the error:
Uncaught (in promise) TypeError: Failed to construct 'Blob': The provided value cannot be converted to a sequence.
I think that I'm doing something wrong and there is more simple way to download file.
Without seeing how you're fetching, it's hard to know. But you should be able to use response.blob() to download the result.
fetch("${api}/channels}", {
method: "GET",
})
.then((response) => response.blob())
.then((blob) => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement("a");
a.href = url;
a.download = "file.xlsx";
document.body.appendChild(a);
a.click();
a.remove();
});
As Joey Ciechanowicz mentioned, we should return response.buffer() from backend and work with its data as blob at frontend.
I mean
NodeJS side (using Got):
const result = got(queryRequestURL, {
headers: headers
});
return await result.buffer()
Frontend side (pure JavaScript):
// fetch data
const result = await fetch(api + path);
return result.blob();
// download file
const filename = 'export.xlsx';
var url = window.URL.createObjectURL(result);
var a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
a.remove();
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.
I have the following function to save a file to the browser using blob...
var saveData = (function () {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return function (data, fileName) {
var json = JSON.stringify(data),
blob = new Blob([json], {type: "octet/stream"}),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
}());
I am passing the following data to it...
var filename = "style.css";
var data = CSSFile //CSS string
saveData(data, filename)
My data is a string of CSS attributes that I want to save as a CSS file. The problem is, when I open the downloaded CSS file, it look like...
"html, body {....} ... "
Notice it is surrounded by double quotes. I need these removed. I tried the following...
CSSFile = CSSFile.substring(0, CSSFile.length - 1);
saveData(data, filename)
But all that did was remove the first character inside the double quotes...
"tml,body{...}..."
How can I save the string without the double quotes?
You are getting quotes in your string because you are running it through JSON.stringify when it is already a string.
Try this instead.
var saveData = (function () {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return function (data, fileName) {
var blob = new Blob([data], {type: "octet/stream"}),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
}());
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
I am working on a web app that opens binary files and allows them to be edited.
This process is basically ondrop -> dataTransfer.files[0] -> FileReader -> Uint8Array
Essentially, I want to be able to save the modified file back as a binary file. Ideally as a file download with a specified file name.
There doesn't seem to be any standard method of doing this, and that sucks, because everything up to that point is well supported.
I am currently converting the array to a string using String.fromCharCode(), base64 encoding that, and using a data uri in a hyperlink like data:application/octet-stream;base64,.., along with the download attribute to specify filename.
It seems to work, but it's quite hacky and I think converting the raw bytes to a string might introduce encoding issues depending on the byte values. I don't want the data to become corrupt or break the string.
Barring that, is there a better/proper method for getting an array of bytes as a binary file to the user?
These are utilities that I use to download files cross-browser. The nifty thing about this is that you can actually set the download property of a link to the name you want your filename to be.
FYI the mimeType for binary is application/octet-stream
var downloadBlob, downloadURL;
downloadBlob = function(data, fileName, mimeType) {
var blob, url;
blob = new Blob([data], {
type: mimeType
});
url = window.URL.createObjectURL(blob);
downloadURL(url, fileName);
setTimeout(function() {
return window.URL.revokeObjectURL(url);
}, 1000);
};
downloadURL = function(data, fileName) {
var a;
a = document.createElement('a');
a.href = data;
a.download = fileName;
document.body.appendChild(a);
a.style = 'display: none';
a.click();
a.remove();
};
Usage:
downloadBlob(myBinaryBlob, 'some-file.bin', 'application/octet-stream');
(shorter) ES6 version of the top answer:
const downloadURL = (data, fileName) => {
const a = document.createElement('a')
a.href = data
a.download = fileName
document.body.appendChild(a)
a.style.display = 'none'
a.click()
a.remove()
}
const downloadBlob = (data, fileName, mimeType) => {
const blob = new Blob([data], {
type: mimeType
})
const url = window.URL.createObjectURL(blob)
downloadURL(url, fileName)
setTimeout(() => window.URL.revokeObjectURL(url), 1000)
}