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);
}
Related
I can not quite wrap my head around on how to download a PDF from a google spreadsheet PDF Export Weblink. I generated a testing spreadsheet for this case.
I understand that I need to implement encodeURIComponent and/or "Buffer.from" to the blob but however I do it, it only downloads a broken PDF for me.
This is what I currently have in its rawest form. Thank you for your support!
Node JS:
const fetch = require('node-fetch');
var url = "https://docs.google.com/spreadsheets/d/1fLjKASR_g5wsvOjjJi6RclqMVd2o_1On-OfimXtId4E/export?exportFormat=pdf&format=pdf&size=A4&fzr=true&gid=477517973&sheetnames=false&printtitle=false&pagenumbers=false&gridlines=false&portrait=true&fitw=true&fith=true&top_margin=0.20&bottom_margin=0.20&left_margin=0.20&right_margin=0.20";
let blob = await fetch(url).then(r => r.blob());
// then send blob variable to javascript
Javascript:
function downloadURI(name) {
var uri = 'data:application/pdf;base64,' + blob;
var link = document.createElement('a');
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
}
downloadURI("test"+".pdf")
I thought that from var uri = 'data:application/pdf;base64,' + blob; in your script, in this case, it is required to convert the downloaded data as the base64. Although I'm not sure about the relationship between the scripts between Node JS: and Javascript:, in your situation, how about the following modification?
From:
let blob = await fetch(url).then(r => r.blob());
To:
let buf = await fetch(url).then((r) => r.arrayBuffer());
const data = Buffer.from(buf).toString("base64");
By this, you can use data as follows.
var uri = 'data:application/pdf;base64,' + data;
Note:
As the additional information, for example, if you want to download your Spreadsheet as a PDF file using only Javascript, you can also use the following script. But, in this case, the Spreadsheet is required to be publicly shared. Please be careful about this.
async function downloadURI(name) {
var url = "https://docs.google.com/spreadsheets/d/1fLjKASR_g5wsvOjjJi6RclqMVd2o_1On-OfimXtId4E/export?exportFormat=pdf&format=pdf&size=A4&fzr=true&gid=477517973&sheetnames=false&printtitle=false&pagenumbers=false&gridlines=false&portrait=true&fitw=true&fith=true&top_margin=0.20&bottom_margin=0.20&left_margin=0.20&right_margin=0.20";
let blob = await fetch(url).then((r) => r.blob());
var f = new FileReader();
f.readAsDataURL(blob);
f.onload = d => {
var uri = d.target.result;
var link = document.createElement('a');
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
}
}
downloadURI("test"+".pdf")
I'm forced to use an old firefox version 26. I have the below code working perfectly on firefox 72, way more recent, I know. While stepping through the download function, I realize my problem has something to do with the a.click(). While comparing the debugging consoles between browsers I don't notice any differences in the function, but the a.click() isn't triggering the saveAs popup.
Here is the json list format, which has indices equal to n select box elements.
testn:{
val: "pass",
desc: "test description"}
JS
jsonData = $("#myform").serializeArray();
function download(content, fileName, contentType) {
var a = document.createElement("a");
var file = new Blob([content], {type: contentType});
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
}
if (confirm("Save results to <SCRIPT_PATH_LOCATION>")){
download(JSON.stringify(jsonData), 'webform.results.json', 'text/plain');
} else {
return false
}
I figured it out. Modern convenience allowed me to get away with not adequately appending the new element, "a" onto the webform.
JS
jsonData = $("#myform").serializeArray();
function download(content, fileName, contentType) {
var a = document.createElement("a");
//Insert these
document.body.appendChild(a);
a.style = "display: none";
//Insert complete
var file = new Blob([content], {type: contentType});
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
}
if (confirm("Save results to <SCRIPT_PATH_LOCATION>")){
download(JSON.stringify(jsonData), 'webform.results.json', 'text/plain');
} else {
return false
}
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();
How can I make a browser display a "save as dialog" so the user can save the content of a string to a file on his system?
For example:
var myString = "my string with some stuff";
save_to_filesystem(myString,"myString.txt");
Resulting in something like this:
EDIT 2022: Please see other answers regarding File System API
In case anyone is still wondering...
I did it like this:
Save
can't remember my source but it uses the following techniques\features:
html5 download attribute
data URI's
Found the reference:
http://paxcel.net/blog/savedownload-file-using-html5-javascript-the-download-attribute-2/
EDIT:
As you can gather from the comments, this does NOT work in
Internet Explorer (however works in Edge v13 and later)
Opera Mini
http://caniuse.com/#feat=download
There is a new spec called the Native File System API that allows you to do this properly like this:
const result = await window.chooseFileSystemEntries({ type: "save-file" });
There is a demo here, but I believe it is using an origin trial so it may not work in your own website unless you sign up or enable a config flag, and it obviously only works in Chrome. If you're making an Electron app this might be an option though.
There is a javascript library for this, see FileSaver.js on Github
However the saveAs() function won't send pure string to the browser, you need to convert it to blob:
function data2blob(data, isBase64) {
var chars = "";
if (isBase64)
chars = atob(data);
else
chars = data;
var bytes = new Array(chars.length);
for (var i = 0; i < chars.length; i++) {
bytes[i] = chars.charCodeAt(i);
}
var blob = new Blob([new Uint8Array(bytes)]);
return blob;
}
and then call saveAs on the blob, as like:
var myString = "my string with some stuff";
saveAs( data2blob(myString), "myString.txt" );
Of course remember to include the above-mentioned javascript library on your webpage using <script src=FileSaver.js>
This is possible using this cross browser javascript implementation of the HTML5 saveAs function: https://github.com/koffsyrup/FileSaver.js
If all you want to do is save text then the above script works in all browsers(including all versions of IE), using nothing but JS.
Solution using only javascript
function saveFile(fileName,urlFile){
let a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
a.href = urlFile;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
a.remove();
}
let textData = `El contenido del archivo
que sera descargado`;
let blobData = new Blob([textData], {type: "text/plain"});
let url = window.URL.createObjectURL(blobData);
//let url = "pathExample/localFile.png"; // LocalFileDownload
saveFile('archivo.txt',url);
Using showSaveFilePicker():
const handle = await showSaveFilePicker({
suggestedName: 'name.txt',
types: [{
description: 'Text file',
accept: {'text/plain': ['.txt']},
}],
});
const blob = new Blob(['Some text']);
const writableStream = await handle.createWritable();
await writableStream.write(blob);
await writableStream.close();
Inspired by #ronald-coarite answer, here is my solution:
function saveTxtToFile(fileName: string, textData: string) {
const blobData = new Blob([textData], { type: 'text/plain' });
const urlToBlob = window.URL.createObjectURL(blobData);
const a = document.createElement('a');
a.style.setProperty('display', 'none');
document.body.appendChild(a);
a.href = urlToBlob;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(urlToBlob);
a.remove();
}
saveTxtToFile('myFile.json', JSON.stringify(myJson));
I have an Angular app where by I want to call my API and return a document for download. The document is stored in a MSSQL Db as an image datatype.
I've had success with the API using the following response message code
var ms = new MemoryStream(_document.Document);
var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new StreamContent(ms)};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-word");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = string.Format("{0}.{1}", "testdocument, ".docx");
return response;
This works well if calling myapi/Document in chrome, it will just download the document.
But I want to have a link in my angular app and trigger this download action.
So far I have managed to get it working by creating an "a" tag and forcing a click event using the following code
var a = window.document.createElement('a');
a.href = SettingsService.getAPIURL() + "Document" + '/';
a.download = "test";
// Append anchor to body.
document.body.appendChild(a);
a.click();
// Remove anchor from body
document.body.removeChild(a);
The only problem with this is that I can't add any headers for my security tokens
The other method I have tried but not successful is the following $http request
result.success(function (data, status, headers, config) {
var arr = data.Document; // this is the byte[]
var byteArray = new Uint8Array(arr);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob([byteArray], { type: data.MediaHeaderValue })); // mediaheadervalue eg "application/msword"
a.download = "test.docx";
// Append anchor to body.
document.body.appendChild(a);
a.click();
});