Difficulties with downloading a file with jQuery (GET) - javascript

I have an API-Server who responds to requests like this:
http://localhost:8080/slim3/public/api/v1/files/Test1.jpg
http://localhost:8080/slim3/public/api/v1/files/Test2.txt
...
If I put such URL into my browser I can get the download prompt. Now I'm struggeling to process the download of a file via jQuery / Ajax.
Every thread I found here on Stackoverflow tells me to send back the actual download url and open it via window.location. I don't understand how this is possible
when my server already has the file downloaded for me and I just need to "grab" it somehow on the client-side?
It is clear to me that I can't force the download dialog via jQuery / Javascript. I read this in multiple threads here. But the same threads don't tell me
how I can get the direct download url. Or do I mix things up here unfortunately?
Here is what I have:
Client (jQuery)
$(document).ready(function(){
$(document).on('click', '#file', function(e){
e.preventDefault();
var filename = $(this).data('url');
$.ajax({
type : "GET",
cache: false,
url : "http://localhost:8080/slim3/public/api/v1/files/" + filename,
success : function(data) {
console.log(data) // the console writes nothing
//window.location = "data:application/octet-stream," + encodeURIComponent(data); // not working
//var downloadUrl = data.url; // not working
//window.location = downloadUrl; // // not working
},
error : function(data) {}
});
});
});
Server (PHP)
public function show($request, $response, $args)
{
$file = 'C:\xampp\htdocs\slim3\storage\Test1.jpg';
$res = $response->withHeader('Content-Description', 'File Transfer')
->withHeader('Content-Type', 'application/octet-stream')
->withHeader('Content-Disposition', 'attachment;filename="'.basename($file).'"')
->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate')
->withHeader('Pragma', 'public')
->withHeader('Content-Length', filesize($file));
readfile($file);
return $res;
}
Solution:
Rob pointed me in the right direction. I actually don't need to do an GET Ajax request. So the final jQuery function looks exacty like this and works:
$(document).on('click', '#file', function(e){
e.preventDefault();
var filename = $(this).data('url');
window.location = "http://localhost:80/slimmi/public/api/v1/files/" + filename;
});

In Client,filename variable will be wrong. It should be Test1.jpgor Test2.txt. I think that $(this).data('url'); returns the current url instead of Test1.jpgor Test2.txtnames. Do you try to substract the file name by using:
var url = $(this).data('url');
var filename = url.substring(url.lastIndexOf("/") + 1, url.length);

Your server just sends back the actual file requested by name in the URL right?
It looks to me like you just need to replace all of the ajax code with
document.location = "http://localhost:8080/slim3/public/api/v1/files/" + filename;
The headers that you set in the PHP will determine whether the browser shows the save dialog or attempts to display the file - those look right.

What you could do if the files are generated on demand is have PHP encode your file in Base64 - like this, setting the appropriate type - and return that to the client. Convert the Base64 to a Blob - you can put Base64 in an anchor's href but IE has a prohibitively small URI size - then create a URL object from that Blob. Among other things this ensures that the data is URL safe. Finally, create an "invisible" anchor tab and click it.
$.ajax({
type: "GET",
url: target,
success: function (response) {
// create a download anchor tag
var downloadLink = document.createElement('a');
downloadLink.target = '_blank';
downloadLink.download = 'your-file-name-here';
// convert Base64 to Blob - don't forget to set content type!
var blob = b64toBlob(response, [file type here]);
// create an object URL from the Blob
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
// set object URL as the anchor's href
downloadLink.href = downloadUrl;
// append the anchor to document body
document.body.appendChild(downloadLink);
// fire a click event on the anchor
downloadLink.click();
// cleanup: remove element and revoke object URL
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
}
});
Convert the Base64 to Blob like this - source.
function b64toBlob(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;
}
This is what I use to download PDF's generated on demand by our Django server and she seems to run pretty darn well.
Addendum
The reason why our website does it this way instead of just returning the file name for a subsequent call is because it's a bit easier on the server I/O. The solution that was chosen means that the requested file has to exist somewhere on the server - most likely on disk. (One might be able to keep the generated file in memory using PHP's tmpfile() but my knowledge of PHP is limited so I do not know how you would keep that file around between HTTP calls).
My project makes big honking PDF's - possibly hundreds of pages. I really, really don't want to have to make an actual file object out of this data, save it to disk, and then almost immediately read it back off the disk (I am aware that that isn't exactly how the server is doing it, but anyway you slice it it's doing more work than necessary). The server has the PDF made, it's in memory, why not just ... give it back to the client?
Returning files like this means that one doesn't need to do any extra clean up work - once the Base64 has left the building, that's it. There's no file on disk so there's nothing that has to be dealt with later (good or bad depending on your needs).

Related

Save a JSON file to server with javascript application

I'm developing a simple Javascript application where the user has some images (stored in my machine) and he is able to annotate them and then save the annotations as a JSON file.
The application is very light and simple and it is not an app server.
However, I need to save those JSON files to the machine that will be behaving as the server.
Since I cannot use Javascript for IO, is there any easy and simple way to save those files without having to implement an app server?
I used Blob to download the files.
function project_save_confirmed(input) {
if ( input.project_name.value !== _onco_settings.project.name ) {
project_set_name(input.project_name.value);
}
// onco project
var _onco_project = { '_onco_settings': _onco_settings,
'_onco_img_metadata': _onco_img_metadata,
'_onco_attributes': _onco_attributes };
var filename = input.project_name.value + '.json';
var data_blob = new Blob( [JSON.stringify(_onco_project)],
{type: 'text/json;charset=utf-8'});
save_data_to_local_file(data_blob, filename);
user_input_default_cancel_handler();
}
function save_data_to_local_file(data, filename) {
var a = document.createElement('a');
a.href = URL.createObjectURL(data);
a.download = filename;
a.click();
}
Any suggestion?
Kind regards!
Copy paste from: Download JSON object as a file from browser
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();
}
This I believe accomplishes what you want, just makes sure that the proper headers are set, push it to an <a> tag, then click() it
You can do this in php:
<?php
//notice this will put WHATEVER is in json into file
$filename="config.json";
if (isset($_POST["json"])) {
file_put_contents($filename,$_POST["json"]);
}
?>
then for the JS side:
var fd=new FormData();
fd.append("json", JSON.stringify(_onco_project));
fetch("https://url.com",{method:"POST",body:fd})
Explanation: JS makes a new formdata, and sets "json" to the stringified json, and sends it off to the server. The php server takes this, and puts it directly into $filename. Make sure data is safe before putting it to file, as it will take whatever it is given and put it into your file!

How get base64 string from the application/octet-stream using .net or javascript? [duplicate]

I am trying to build a PDF file out of a binary stream which I receive as a response from an Ajax request.
Via XmlHttpRequest I receive the following data:
%PDF-1.4....
.....
....hole data representing the file
....
%% EOF
What I tried so far was to embed my data via data:uri. Now, there's nothing wrong with it and it works fine. Unfortunately, it does not work in IE9 and Firefox. A possible reason may be that FF and IE9 have their problems with this usage of the data-uri.
Now, I'm looking for any solution that works for all browsers. Here's my code:
// responseText encoding
pdfText = $.base64.decode($.trim(pdfText));
// Now pdfText contains %PDF-1.4 ...... data...... %%EOF
var winlogicalname = "detailPDF";
var winparams = 'dependent=yes,locationbar=no,scrollbars=yes,menubar=yes,'+
'resizable,screenX=50,screenY=50,width=850,height=1050';
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf,'
+ escape(pdfText)
+ '"></embed>';
// Open PDF in new browser window
var detailWindow = window.open ("", winlogicalname, winparams);
detailWindow.document.write(htmlText);
detailWindow.document.close();
As I have said, it works fine with Opera and Chrome (Safari hasn't been tested). Using IE or FF will bring up a blank new window.
Is there any solution like building a PDF file on a file system
in order to let the user download it? I need the solution that works in all browsers, at least in IE, FF, Opera, Chrome and Safari.
I have no permission to edit the web-service implementation. So it had to be a solution at client-side. Any ideas?
Is there any solution like building a pdf file on file system in order
to let the user download it?
Try setting responseType of XMLHttpRequest to blob , substituting download attribute at a element for window.open to allow download of response from XMLHttpRequest as .pdf file
var request = new XMLHttpRequest();
request.open("GET", "/path/to/pdf", true);
request.responseType = "blob";
request.onload = function (e) {
if (this.status === 200) {
// `blob` response
console.log(this.response);
// create `objectURL` of `this.response` : `.pdf` as `Blob`
var file = window.URL.createObjectURL(this.response);
var a = document.createElement("a");
a.href = file;
a.download = this.response.name || "detailPDF";
document.body.appendChild(a);
a.click();
// remove `a` following `Save As` dialog,
// `window` regains `focus`
window.onfocus = function () {
document.body.removeChild(a)
}
};
};
request.send();
I realize this is a rather old question, but here's the solution I came up with today:
doSomethingToRequestData().then(function(downloadedFile) {
// create a download anchor tag
var downloadLink = document.createElement('a');
downloadLink.target = '_blank';
downloadLink.download = 'name_to_give_saved_file.pdf';
// convert downloaded data to a Blob
var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });
// create an object URL from the Blob
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
// set object URL as the anchor's href
downloadLink.href = downloadUrl;
// append the anchor to document body
document.body.append(downloadLink);
// fire a click event on the anchor
downloadLink.click();
// cleanup: remove element and revoke object URL
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
}
I changed this:
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf,'
+ escape(pdfText)
+ '"></embed>';
to
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf;base64,'
+ escape(pdfText)
+ '"></embed>';
and it worked for me.
The answer of #alexandre with base64 does the trick.
The explanation why that works for IE is here
https://en.m.wikipedia.org/wiki/Data_URI_scheme
Under header 'format' where it says
Some browsers (Chrome, Opera, Safari, Firefox) accept a non-standard
ordering if both ;base64 and ;charset are supplied, while Internet
Explorer requires that the charset's specification must precede the
base64 token.
I work in PHP and use a function to decode the binary data sent back from the server. I extract the information an input to a simple file.php and view the file through my server and all browser display the pdf artefact.
<?php
$data = 'dfjhdfjhdfjhdfjhjhdfjhdfjhdfjhdfdfjhdf==blah...blah...blah..'
$data = base64_decode($data);
header("Content-type: application/pdf");
header("Content-Length:" . strlen($data ));
header("Content-Disposition: inline; filename=label.pdf");
print $data;
exit(1);
?>
Detect the browser and use Data-URI for Chrome and use PDF.js as below for other browsers.
PDFJS.getDocument(url_of_pdf)
.then(function(pdf) {
return pdf.getPage(1);
})
.then(function(page) {
// get a viewport
var scale = 1.5;
var viewport = page.getViewport(scale);
// get or create a canvas
var canvas = ...;
canvas.width = viewport.width;
canvas.height = viewport.height;
// render a page
page.render({
canvasContext: canvas.getContext('2d'),
viewport: viewport
});
})
.catch(function(err) {
// deal with errors here!
});
I saw another question on just this topic recently (streaming pdf into iframe using dataurl only works in chrome).
I've constructed pdfs in the ast and streamed them to the browser. I was creating them first with fdf, then with a pdf class I wrote myself - in each case the pdf was created from data retrieved from a COM object based on a couple of of GET params passed in via the url.
From looking at your data sent recieved in the ajax call, it looks like you're nearly there. I haven't played with the code for a couple of years now and didn't document it as well as I'd have cared to, but - I think all you need to do is set the target of an iframe to be the url you get the pdf from. Though this may not work - the file that oututs the pdf may also have to outut a html response header first.
In a nutshell, this is the output code I used:
//We send to a browser
header('Content-Type: application/pdf');
if(headers_sent())
$this->Error('Some data has already been output, can\'t send PDF file');
header('Content-Length: '.strlen($this->buffer));
header('Content-Disposition: inline; filename="'.$name.'"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
ini_set('zlib.output_compression','0');
echo $this->buffer;
So, without seeing the full response text fro the ajax call I can't really be certain what it is, though I'm inclined to think that the code that outputs the pdf you're requesting may only be doig the equivalent of the last line in the above code. If it's code you have control over, I'd try setting the headers - then this way the browser can just deal with the response text - you don't have to bother doing a thing to it.
I simply constructed a url for the pdf I wanted (a timetable) then created a string that represented the html for an iframe of the desired sie, id etc that used the constructed url as it's src. As soon as I set the inner html of a div to the constructed html string, the browser asked for the pdf and then displayed it when it was received.
function showPdfTt(studentId)
{
var url, tgt;
title = byId("popupTitle");
title.innerHTML = "Timetable for " + studentId;
tgt = byId("popupContent");
url = "pdftimetable.php?";
url += "id="+studentId;
url += "&type=Student";
tgt.innerHTML = "<iframe onload=\"centerElem(byId('box'))\" src='"+url+"' width=\"700px\" height=\"500px\"></iframe>";
}
EDIT: forgot to mention - you can send binary pdf's in this manner. The streams they contain don't need to be ascii85 or hex encoded. I used flate on all the streams in the pdf and it worked fine.
You can use PDF.js to create PDF files from javascript... it's easy to code... hope this solve your doubt!!!
Regards!

How can i create a file and save it in local system using Blob in javascript?

During implementation of file transfer use case through WebRTC protocol, i am recieving data from queue at reciever end in some variables but unable to use that. Through some digging, i came to know that it can be done using Blob,
code snippet that i used :
var data=reciever.dequeue();
if(data)
{ var blob = new Blob(_base64ToArrayBuffer(data), {type: 'text/plain'});
// need to know how to proceed now?
}
file is need to be saved in local system.Thanks in advance.
You can create a temp anchor element element and append it to document.body, trigger click event. Done.
Here is the demo code:
var url = objectURL = URL.createObjectURL(blob); //your blob object here
var anchor = document.createElement("a");
anchor.href = url;
anchor.download = "YourFileName";
anchor.click(); //This will trigger browser download event.
Here is document about blob to URL.
Here is the blob document.
Hope this works. : )

Downloading a zip file using angular

I'm trying to implement the download of a file through angular.js
The file comes from the server in binary format, the content type is application/octet-stream
The download is a GET using $resource. Looking at the parameter passed to the callback (called content below), it's an object containing the byte array and also a list of properties for $resource.
Tried several ways to serve the file, but without success.
First of them:
...
var a = document.createElement('a');
a.href = "data:attachment/zip," + content;
a.download = zipName;
a.click();
In this case, the content of the zip file is [object Object]
I tried extracting the array from the object and joining everything into a string variable. The zip file in this case is way larger than the normal size. I had to set isArray: true in the service that calls $resource, otherwise there was no way to extract the byte content from the response object.
Here is how I did it:
var str = '';
for (var i = 0; i < content.length; i++) {
str += content[i][0];
}
...
var a = document.createElement('a');
a.href = "data:attachment/zip," + str;
a.download = zipName;
a.click();
Worth mentioning that calling encodeURI on str increments drastically the size of the downloaded zip, but the archive remains invalid.
I also tried creating a Blob from the str and setting the content type to application/octet-stream, without any luck.
var blob = new Blob([str], {'type':"application/octet-stream"});
a.href = window.URL.createObjectURL(blob);
...
Don't know what I'm missing here, but it looks rather a problem of getting the right format for the byte array content and setting the correct href before simulating the click for downloading.
Help is appreciated.
Thanks
I just found your post and fixed an answer using what you enlist.
First you have to ensure that your angular $http request includes, like the following get example (include responseType: 'arraybuffer')
$http.get('/downloadZip', {
params: {
file: encodeURIComponent(filepath)
},
responseType: 'arraybuffer'
//your code
Second on your success or promise handler you should change your window.URL.createObjectURL(blob) to URL.createObjectURL(blob). Implementing something similar to the following:
var a = document.createElement('a');
var blob = new Blob([data], {'type':"application/octet-stream"});
a.href = URL.createObjectURL(blob);
a.download = "filename.zip";
a.click();
With these you are creating a new anchor element and simulating to opening it. With a correct Blob creation since the request had been modified correctly.
Angular is no needed.
var zip_file_path = "" //put inside "" your server 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);
In case anyone is still on AngularJS (like me) and wants to do this, I took David's answer and made it work with the angular $resource instead of using the lower level $http directly. If you use $resource, this should help you:
var myReportingResource = $resource(baseURL + '/mypath/:command', {},{
getExportZip: {
method: 'GET',
params: {
command: 'exportzip'
},
responseType: 'arraybuffer',
// don't try to convert the zip to JSON
// instead take the data that comes back and put it in an object under a content key
transformResponse: function(data){
return {content: data};
}
}
});
// call the resource like this
myReportingResource.getExportZip(yourParams).$promise.then(function(zipData){
// create a anchor element, stick the zip data in it, and click it to download
var anchor = angular.element('<a/>');
anchor.attr({
href: URL.createObjectURL(new Blob([zipData.content], {'type':'application/octet-stream'})),
download: 'myfilename.zip'
})[0].click();
});
You need the transformResponse bit because otherwise AngularJS will convert your response to JSON- which is wrong with binary data. This is why later you use zipData.content to pass the data into the Blob. You can get rid of the content part, it's there for simplicity with my error handling code.
This works in Chrome and Safari as of May 2019. Didn't test anywhere else.

Using HTML5/JavaScript to generate and save a file

I've been fiddling with WebGL lately, and have gotten a Collada reader working. Problem is it's pretty slow (Collada is a very verbose format), so I'm going to start converting files to a easier to use format (probably JSON). I already have the code to parse the file in JavaScript, so I may as well use it as my exporter too! The problem is saving.
Now, I know that I can parse the file, send the result to the server, and have the browser request the file back from the server as a download. But in reality the server has nothing to do with this particular process, so why get it involved? I already have the contents of the desired file in memory. Is there any way that I could present the user with a download using pure JavaScript? (I doubt it, but might as well ask...)
And to be clear: I am not trying to access the filesystem without the users knowledge! The user will provide a file (probably via drag and drop), the script will transform the file in memory, and the user will be prompted to download the result. All of which should be "safe" activities as far as the browser is concerned.
[EDIT]: I didn't mention it upfront, so the posters who answered "Flash" are valid enough, but part of what I'm doing is an attempt to highlight what can be done with pure HTML5... so Flash is right out in my case. (Though it's a perfectly valid answer for anyone doing a "real" web app.) That being the case it looks like I'm out of luck unless I want to involve the server. Thanks anyway!
Simple solution for HTML5 ready browsers...
function download(filename, text) {
var pom = document.createElement('a');
pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
pom.setAttribute('download', filename);
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
}
else {
pom.click();
}
}
Usage
download('test.txt', 'Hello world!');
OK, creating a data:URI definitely does the trick for me, thanks to Matthew and Dennkster pointing that option out! Here is basically how I do it:
1) get all the content into a string called "content" (e.g. by creating it there initially or by reading innerHTML of the tag of an already built page).
2) Build the data URI:
uriContent = "data:application/octet-stream," + encodeURIComponent(content);
There will be length limitations depending on browser type etc., but e.g. Firefox 3.6.12 works until at least 256k. Encoding in Base64 instead using encodeURIComponent might make things more efficient, but for me that was ok.
3) open a new window and "redirect" it to this URI prompts for a download location of my JavaScript generated page:
newWindow = window.open(uriContent, 'neuesDokument');
That's it.
HTML5 defined a window.saveAs(blob, filename) method. It isn't supported by any browser right now. But there is a compatibility library called FileSaver.js that adds this function to most modern browsers (including Internet Explorer 10+). Internet Explorer 10 supports a navigator.msSaveBlob(blob, filename) method (MSDN), which is used in FileSaver.js for Internet Explorer support.
I wrote a blog posting with more details about this problem.
Saving large files
Long data URIs can give performance problems in browsers. Another option to save client-side generated files, is to put their contents in a Blob (or File) object and create a download link using URL.createObjectURL(blob). This returns an URL that can be used to retrieve the contents of the blob. The blob is stored inside the browser until either URL.revokeObjectURL() is called on the URL or the document that created it is closed. Most web browsers have support for object URLs, Opera Mini is the only one that does not support them.
Forcing a download
If the data is text or an image, the browser can open the file, instead of saving it to disk. To cause the file to be downloaded upon clicking the link, you can use the the download attribute. However, not all web browsers have support for the download attribute. Another option is to use application/octet-stream as the file's mime-type, but this causes the file to be presented as a binary blob which is especially user-unfriendly if you don't or can't specify a filename. See also 'Force to open "Save As..." popup open at text link click for pdf in HTML'.
Specifying a filename
If the blob is created with the File constructor, you can also set a filename, but only a few web browsers (including Chrome & Firefox) have support for the File constructor. The filename can also be specified as the argument to the download attribute, but this is subject to a ton of security considerations. Internet Explorer 10 and 11 provides its own method, msSaveBlob, to specify a filename.
Example code
var file;
var data = [];
data.push("This is a test\n");
data.push("Of creating a file\n");
data.push("In a browser\n");
var properties = {type: 'text/plain'}; // Specify the file's mime-type.
try {
// Specify the filename using the File constructor, but ...
file = new File(data, "file.txt", properties);
} catch (e) {
// ... fall back to the Blob constructor if that isn't supported.
file = new Blob(data, properties);
}
var url = URL.createObjectURL(file);
document.getElementById('link').href = url;
<a id="link" target="_blank" download="file.txt">Download</a>
function download(content, filename, contentType)
{
if(!contentType) contentType = 'application/octet-stream';
var a = document.createElement('a');
var blob = new Blob([content], {'type':contentType});
a.href = window.URL.createObjectURL(blob);
a.download = filename;
a.click();
}
Take a look at Doug Neiner's Downloadify which is a Flash based JavaScript interface to do this.
Downloadify is a tiny JavaScript + Flash library that enables the generation and saving of files on the fly, in the browser, without server interaction.
Simple Solution!
<a download="My-FileName.txt" href="data:application/octet-stream,HELLO-WORLDDDDDDDD">Click here</a>
Works in all Modern browsers.
I've used FileSaver (https://github.com/eligrey/FileSaver.js) and it works just fine.
For example, I did this function to export logs displayed on a page.
You have to pass an array for the instanciation of the Blob, so I just maybe didn't write this the right way, but it works for me.
Just in case, be careful with the replace: this is the syntax to make this global, otherwise it will only replace the first one he meets.
exportLogs : function(){
var array = new Array();
var str = $('#logs').html();
array[0] = str.replace(/<br>/g, '\n\t');
var blob = new Blob(array, {type: "text/plain;charset=utf-8"});
saveAs(blob, "example.log");
}
You can generate a data URI. However, there are browser-specific limitations.
I found two simple approaches that work for me. First, using an already clicked a element and injecting the download data. And second, generating an a element with the download data, executing a.click() and removing it again. But the second approach works only if invoked by a user click action as well. (Some) Browser block click() from other contexts like on loading or triggered after a timeout (setTimeout).
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
function linkDownload(a, filename, content) {
contentType = 'data:application/octet-stream,';
uriContent = contentType + encodeURIComponent(content);
a.setAttribute('href', uriContent);
a.setAttribute('download', filename);
}
function download(filename, content) {
var a = document.createElement('a');
linkDownload(a, filename, content);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
</script>
</head>
<body>
download
<button onclick="download('test.txt', 'Hello World!');">download</button>
</body>
</html>
try
let a = document.createElement('a');
a.href = "data:application/octet-stream,"+encodeURIComponent('"My DATA"');
a.download = 'myFile.json';
a.click(); // we not add 'a' to DOM so no need to remove
If you want to download binary data look here
Update
2020.06.14 I upgrade Chrome to 83.0 and above SO snippet stop works (due to sandbox security restrictions) - but JSFiddle version works - here
Here is a link to the data URI method Mathew suggested, it worked on safari, but not well because I couldn't set the filetype, it gets saved as "unknown" and then i have to go there again later and change it in order to view the file...
http://www.nihilogic.dk/labs/canvas2image/
You can use localStorage. This is the Html5 equivalent of cookies. It appears to work on Chrome and Firefox BUT on Firefox, I needed to upload it to a server. That is, testing directly on my home computer didn't work.
I'm working up HTML5 examples. Go to http://faculty.purchase.edu/jeanine.meyer/html5/html5explain.html
and scroll to the maze one. The information to re-build the maze is stored using localStorage.
I came to this article looking for HTML5 JavaScript for loading and working with xml files. Is it the same as older html and JavaScript????
As previously mentioned the File API, along with the FileWriter and FileSystem APIs can be used to store files on a client's machine from the context of a browser tab/window.
However, there are several things pertaining to latter two APIs which you should be aware of:
Implementations of the APIs currently exist only in Chromium-based browsers (Chrome & Opera)
Both of the APIs were taken off of the W3C standards track on April 24, 2014, and as of now are proprietary
Removal of the (now proprietary) APIs from implementing browsers in the future is a possibility
A sandbox (a location on disk outside of which files can produce no effect) is used to store the files created with the APIs
A virtual file system (a directory structure which does not necessarily exist on disk in the same form that it does when accessed from within the browser) is used represent the files created with the APIs
Here are simple examples of how the APIs are used, directly and indirectly, in tandem to do this:
BakedGoods*
bakedGoods.get({
data: ["testFile"],
storageTypes: ["fileSystem"],
options: {fileSystem:{storageType: Window.PERSISTENT}},
complete: function(resultDataObj, byStorageTypeErrorObj){}
});
Using the raw File, FileWriter, and FileSystem APIs
function onQuotaRequestSuccess(grantedQuota)
{
function saveFile(directoryEntry)
{
function createFileWriter(fileEntry)
{
function write(fileWriter)
{
var dataBlob = new Blob(["Hello world!"], {type: "text/plain"});
fileWriter.write(dataBlob);
}
fileEntry.createWriter(write);
}
directoryEntry.getFile(
"testFile",
{create: true, exclusive: true},
createFileWriter
);
}
requestFileSystem(Window.PERSISTENT, grantedQuota, saveFile);
}
var desiredQuota = 1024 * 1024 * 1024;
var quotaManagementObj = navigator.webkitPersistentStorage;
quotaManagementObj.requestQuota(desiredQuota, onQuotaRequestSuccess);
Though the FileSystem and FileWriter APIs are no longer on the standards track, their use can be justified in some cases, in my opinion, because:
Renewed interest from the un-implementing browser vendors may place them right back on it
Market penetration of implementing (Chromium-based) browsers is high
Google (the main contributer to Chromium) has not given and end-of-life date to the APIs
Whether "some cases" encompasses your own, however, is for you to decide.
*BakedGoods is maintained by none other than this guy right here :)
This thread was invaluable to figure out how to generate a binary file and prompt to download the named file, all in client code without a server.
First step for me was generating the binary blob from data that I was saving. There's plenty of samples for doing this for a single binary type, in my case I have a binary format with multiple types which you can pass as an array to create the blob.
saveAnimation: function() {
var device = this.Device;
var maxRow = ChromaAnimation.getMaxRow(device);
var maxColumn = ChromaAnimation.getMaxColumn(device);
var frames = this.Frames;
var frameCount = frames.length;
var writeArrays = [];
var writeArray = new Uint32Array(1);
var version = 1;
writeArray[0] = version;
writeArrays.push(writeArray.buffer);
//console.log('version:', version);
var writeArray = new Uint8Array(1);
var deviceType = this.DeviceType;
writeArray[0] = deviceType;
writeArrays.push(writeArray.buffer);
//console.log('deviceType:', deviceType);
var writeArray = new Uint8Array(1);
writeArray[0] = device;
writeArrays.push(writeArray.buffer);
//console.log('device:', device);
var writeArray = new Uint32Array(1);
writeArray[0] = frameCount;
writeArrays.push(writeArray.buffer);
//console.log('frameCount:', frameCount);
for (var index = 0; index < frameCount; ++index) {
var frame = frames[index];
var writeArray = new Float32Array(1);
var duration = frame.Duration;
if (duration < 0.033) {
duration = 0.033;
}
writeArray[0] = duration;
writeArrays.push(writeArray.buffer);
//console.log('Frame', index, 'duration', duration);
var writeArray = new Uint32Array(maxRow * maxColumn);
for (var i = 0; i < maxRow; ++i) {
for (var j = 0; j < maxColumn; ++j) {
var color = frame.Colors[i][j];
writeArray[i * maxColumn + j] = color;
}
}
writeArrays.push(writeArray.buffer);
}
var blob = new Blob(writeArrays, {type: 'application/octet-stream'});
return blob;
}
The next step is to get the browser to prompt the user to download this blob with a predefined name.
All I needed was a named link I added in the HTML5 that I could reuse to rename the initial filename. I kept it hidden since the link doesn't need display.
<a id="lnkDownload" style="display: none" download="client.chroma" href="" target="_blank"></a>
The last step is to prompt the user to download the file.
var data = animation.saveAnimation();
var uriContent = URL.createObjectURL(data);
var lnkDownload = document.getElementById('lnkDownload');
lnkDownload.download = 'theDefaultFileName.extension';
lnkDownload.href = uriContent;
lnkDownload.click();
When testing the "ahref" method, I found that the web developer tools of Firefox and Chrome gets confused. I needed to restart the debugging after the a.click() was issued. Same happened with the FileSaver (it uses the same ahref method to actually make the saving). To work around it, I created new temporary window, added the element a into that and clicked it there.
function download_json(dt) {
var csv = ' var data = ';
csv += JSON.stringify(dt, null, 3);
var uricontent = 'data:application/octet-stream,' + encodeURI(csv);
var newwin = window.open( "", "_blank" );
var elem = newwin.document.createElement('a');
elem.download = "database.js";
elem.href = uricontent;
elem.click();
setTimeout(function(){ newwin.close(); }, 3000);
}
You can use this to save text and other data:
function downloadFile(name, data) {
let a = document.createElement("a");
if (typeof a.download !== "undefined") a.download = name;
a.href = URL.createObjectURL(new Blob([data], {
type: "application/octet-stream"
}));
a.dispatchEvent(new MouseEvent("click"));
}
This function will create an Anchor element, set the name via .download (if supported), assign a url (.href) created from an object (URL.createObjectURL), in this case a Blob object, and dispatch a click event. In short: it's as if you're clicking a download link.
Example code
downloadFile("textfile.txt", "A simple text file");
downloadFile(
"circle.svg",
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="42" />
</svg>`
);
downloadFile(
"utf8string.txt",
new Uint8Array([85, 84, 70, 45, 56, 32, 115, 116, 114, 105, 110, 103]) // "UTF-8 string"
);
This function also accepts File, Blob and MediaSource:
function downloadFile(name, data) {
if (!(data instanceof File || data instanceof Blob || data instanceof MediaSource)) {
return downloadFile(name, new Blob([data], {
type: "application/octet-stream"
}));
}
let a = document.createElement("a");
if (typeof a.download !== "undefined") a.download = name;
a.href = URL.createObjectURL(data);
a.dispatchEvent(new MouseEvent("click"));
}
Or you could use two functions:
function downloadFile(name, data) {
return downloadObject(new Blob([data], {
type: "application/octet-stream"
}));
}
function downloadObject(name, object) {
let a = document.createElement("a");
if (typeof a.download !== "undefined") a.download = name;
a.href = URL.createObjectURL(object);
a.dispatchEvent(new MouseEvent("click"));
}
Here is a tutorial to export files as ZIP:
Before getting started, there is a library to save files, the name of library is fileSaver.js, You can find this library here. Let's get started, Now, include the required libraries:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.4/jszip.min.js" type="text/javascript"></script>
<script type="text/javascript" src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.js" ></script>
Now copy this code and this code will download a zip file with a file hello.txt having content Hello World. If everything thing works fine, this will download a file.
<script type="text/javascript">
var zip = new JSZip();
zip.file("Hello.txt", "Hello World\n");
zip.generateAsync({type:"blob"})
.then(function(content) {
// see FileSaver.js
saveAs(content, "file.zip");
});
</script>
This will download a file called file.zip. You can read more here: http://www.wapgee.com/story/248/guide-to-create-zip-files-using-javascript-by-using-jszip-library
For simple files like 'txt' or'js' you can use the package fs-browsers.
It has nice and easy download and export methods for client-side which do not invole any server.
import { exportFile } from 'fs-browsers';
const onExportClick = (textToExport) => {
// Export to txt file
exportFile(textToExport);
}
If you want to change the name of the file, or even it's type you can do it easily with this:
import { exportFile } from 'fs-browsers';
const onExportClick = (textToExport) => {
// Export to js file called 'file.js'
exportFile(textToExport, { fileName: 'file.js' });
}
For more complex files you will need to involve a server as you said.
The package can also does that with excel files ('xls') if that is what you need.
import { exportFile, EXCEL_FILE } from 'fs-browsers';
const data = [{ "id": 5, "name": "John", "grade": 90, "age": 15 }, { "id": 7, "name": "Nick", "grade": 70, "age": 17 }];
const headings = ["Student ID", "Student Name", "Test Grade", "Student Age"];
exportFile(data, { type: EXCEL_FILE, headings: headings, fileName: 'grades.xls' });
Maybe in the future there eill be other kind of files too.

Categories