How to get downlaod progress with fetch()? [duplicate] - javascript

I am trying to download the large json data.
But it leads to Uncaught RangeError: Invalid string length.
Pls help to solve this problem,Thanks in advance.
Here is the Jsfiddle : http://jsfiddle.net/sLq3F/456/

You can use fetch(), Response.body.getReader() which returns a ReadableStream, TextDecoder(), Blob(), URL.createObjectURL().
Note, at device having limited RAM or low available disk space, after clicking Save at Save File dialog four minutes and twenty seconds 4:20 elapsed before the Save File dialog closed, followed by an additional one minute and thirty seconds 1:30 before the .crdownload extension was removed from the file at file manager GUI. During the first 4:20 period where the file is downloading to filesystem and the Save File dialog is visible the mouse pointer is movable, though the UI is temporarily unresponsive to clicks or attempts to change tabs. When the Save File dialog closes and the the file is still downloading to filesystem, having extension .crdownload the UI returns to normal functionality.
At the conclusion of the process described above the file was successfully downloaded to local filesystem having a total size of 189.8 MB (189,778,220 bytes).
<!DOCTYPE html>
<html>
<head>
<style>
code {
color:navy;
background-color:#eee;
padding:2px;
}
</style>
</head>
<body>
<button>Request File</button><br>
<progress min="0" max="189778220" value="0"></progress>
<output></output>
<br><br>
<label></label>
<script>
var url = "https://raw.githubusercontent.com/zemirco/sf-city-lots-json/master/citylots.json";
var button = document.querySelector("button");
var progress = document.querySelector("progress");
var label = document.querySelector("label");
var output = document.querySelector("output");
var request = (url) => {
label.innerHTML = `Requesting <code>${url}</code> at ${new Date()}.<br><br>`;
return fetch(url)
.then(response => response.body.getReader())
.then(reader => {
var decoder = new TextDecoder();
var json = "";
label.innerHTML += `Request successful.<br><br>Reading request body at ${new Date()}.<br><br>`;
return reader.read().then(function processData(result) {
if (result.done) {
// do stuff when `reader` is `closed`
return reader.closed.then(function() {
// return `json` string
return json;
});
};
json += decoder.decode(result.value);
output.innerHTML = ` ${json.length} of ${progress.max} bytes read`;
progress.value = json.length;
return reader.read().then(processData)
})
.then(function(data) {
var message = `Reading of <code>${url}</code> complete at ${new Date()}. <br><br>`
+ `${data.length} total bytes read. `
+ `Please allow up to 4 minutes for file to download `
+ `to filesystem after clicking <code>Save</code>.<br><br>`;
label.innerHTML += message;
var blob = new Blob([data], {
type: "application/json"
});
var file = URL.createObjectURL(blob);
var a = document.createElement("a");
a.download = "citylots.json";
a.href = file;
document.body.appendChild(a);
a.click();
var closeBlob = (e) => {
window.removeEventListener("focus", closeBlob);
blob.close();
URL.revokeObjectURL(file);
};
window.addEventListener("focus", closeBlob);
return message.replace(/<[^>]*>/g, "");
})
.catch(function(err) {
console.log("err", err)
})
});
}
var handleRequest = (e) => {
button.setAttribute("disabled", "disabled");
request(url).then(function(message) {
console.log(message);
button.removeAttribute("disabled");
})
};
button.addEventListener("click", handleRequest);
</script>
</body>
</html>
plnkr https://plnkr.co/edit/gewixzHZSKRXquZ2OVF2?p=preview

I guess you would need to loop through something in the JSON file and split it up into more manageable strings.
Tom
Do you have an example snippet the JSON file?

Do you have a server running on PHP?
if you do, I believe you must absolutely check your PHP.ini file or run a phpinfo page because php settings does limit the size of files transfer even if it isnt an external request. It could do that in other languages but I never had this problem except on PHP.
P.S. I did not see the size of the file

I think your json file is one of the "TOO BIG FOR JSON" scenarios. you know, if json file have so many records ( to be exact, based on a test, 100000 records make the browser hang and more then that failed to load in many browsers) its not recommend to use.
you can read this article for more information : HOW BIG IS TOO BIG FOR JSON?

Related

How to write data in JSON file in the EJS template file [duplicate]

I want to Write Data to existing file using JavaScript.
I don't want to print it on console.
I want to Actually Write data to abc.txt.
I read many answered question but every where they are printing on console.
at some place they have given code but its not working.
So please can any one help me How to actually write data to File.
I referred the code but its not working:
its giving error:
Uncaught TypeError: Illegal constructor
on chrome and
SecurityError: The operation is insecure.
on Mozilla
var f = "sometextfile.txt";
writeTextFile(f, "Spoon")
writeTextFile(f, "Cheese monkey")
writeTextFile(f, "Onion")
function writeTextFile(afilename, output)
{
var txtFile =new File(afilename);
txtFile.writeln(output);
txtFile.close();
}
So can we actually write data to file using only Javascript or NOT?
You can create files in browser using Blob and URL.createObjectURL. All recent browsers support this.
You can not directly save the file you create, since that would cause massive security problems, but you can provide it as a download link for the user. You can suggest a file name via the download attribute of the link, in browsers that support the download attribute. As with any other download, the user downloading the file will have the final say on the file name though.
var textFile = null,
makeTextFile = function (text) {
var data = new Blob([text], {type: 'text/plain'});
// If we are replacing a previously generated file we need to
// manually revoke the object URL to avoid memory leaks.
if (textFile !== null) {
window.URL.revokeObjectURL(textFile);
}
textFile = window.URL.createObjectURL(data);
// returns a URL you can use as a href
return textFile;
};
Here's an example that uses this technique to save arbitrary text from a textarea.
If you want to immediately initiate the download instead of requiring the user to click on a link, you can use mouse events to simulate a mouse click on the link as Lifecube's answer did. I've created an updated example that uses this technique.
var create = document.getElementById('create'),
textbox = document.getElementById('textbox');
create.addEventListener('click', function () {
var link = document.createElement('a');
link.setAttribute('download', 'info.txt');
link.href = makeTextFile(textbox.value);
document.body.appendChild(link);
// wait for the link to be added to the document
window.requestAnimationFrame(function () {
var event = new MouseEvent('click');
link.dispatchEvent(event);
document.body.removeChild(link);
});
}, false);
Some suggestions for this -
If you are trying to write a file on client machine, You can't do this in any cross-browser way. IE does have methods to enable "trusted" applications to use ActiveX objects to read/write file.
If you are trying to save it on your server then simply pass on the text data to your server and execute the file writing code using some server side language.
To store some information on the client side that is considerably small, you can go for cookies.
Using the HTML5 API for Local Storage.
If you are talking about browser javascript, you can not write data directly to local file for security reason. HTML 5 new API can only allow you to read files.
But if you want to write data, and enable user to download as a file to local. the following code works:
function download(strData, strFileName, strMimeType) {
var D = document,
A = arguments,
a = D.createElement("a"),
d = A[0],
n = A[1],
t = A[2] || "text/plain";
//build download link:
a.href = "data:" + strMimeType + "charset=utf-8," + escape(strData);
if (window.MSBlobBuilder) { // IE10
var bb = new MSBlobBuilder();
bb.append(strData);
return navigator.msSaveBlob(bb, strFileName);
} /* end if(window.MSBlobBuilder) */
if ('download' in a) { //FF20, CH19
a.setAttribute("download", n);
a.innerHTML = "downloading...";
D.body.appendChild(a);
setTimeout(function() {
var e = D.createEvent("MouseEvents");
e.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(e);
D.body.removeChild(a);
}, 66);
return true;
}; /* end if('download' in a) */
//do iframe dataURL download: (older W3)
var f = D.createElement("iframe");
D.body.appendChild(f);
f.src = "data:" + (A[2] ? A[2] : "application/octet-stream") + (window.btoa ? ";base64" : "") + "," + (window.btoa ? window.btoa : escape)(strData);
setTimeout(function() {
D.body.removeChild(f);
}, 333);
return true;
}
to use it:
download('the content of the file', 'filename.txt', 'text/plain');
Try
let a = document.createElement('a');
a.href = "data:application/octet-stream,"+encodeURIComponent("My DATA");
a.download = 'abc.txt';
a.click();
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 (reason: sandbox security restrictions) - but JSFiddle version works - here
Above answer is useful but, I found code which helps you to download text file directly on button click.
In this code you can also change filename as you wish. It's pure javascript function with HTML5.
Works for me!
function saveTextAsFile()
{
var textToWrite = document.getElementById("inputTextToSave").value;
var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
var fileNameToSaveAs = document.getElementById("inputFileNameToSaveAs").value;
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = "Download File";
if (window.webkitURL != null)
{
// Chrome allows the link to be clicked
// without actually adding it to the DOM.
downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
}
else
{
// Firefox requires the link to be added to the DOM
// before it can be clicked.
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
downloadLink.click();
}
const data = {name: 'Ronn', age: 27}; //sample json
const a = document.createElement('a');
const blob = new Blob([JSON.stringify(data)]);
a.href = URL.createObjectURL(blob);
a.download = 'sample-profile'; //filename to download
a.click();
Check Blob documentation here - Blob MDN to provide extra parameters for file type. By default it will make .txt file
In the case it is not possibile to use the new Blob solution, that is for sure the best solution in modern browser, it is still possible to use this simpler approach, that has a limit in the file size by the way:
function download() {
var fileContents=JSON.stringify(jsonObject, null, 2);
var fileName= "data.json";
var pp = document.createElement('a');
pp.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContents));
pp.setAttribute('download', fileName);
pp.click();
}
setTimeout(function() {download()}, 500);
$('#download').on("click", function() {
function download() {
var jsonObject = {
"name": "John",
"age": 31,
"city": "New York"
};
var fileContents = JSON.stringify(jsonObject, null, 2);
var fileName = "data.json";
var pp = document.createElement('a');
pp.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContents));
pp.setAttribute('download', fileName);
pp.click();
}
setTimeout(function() {
download()
}, 500);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="download">Download me</button>
Use the code by the user #useless-code above (https://stackoverflow.com/a/21016088/327386) to generate the file.
If you want to download the file automatically, pass the textFile that was just generated to this function:
var downloadFile = function downloadURL(url) {
var hiddenIFrameID = 'hiddenDownloader',
iframe = document.getElementById(hiddenIFrameID);
if (iframe === null) {
iframe = document.createElement('iframe');
iframe.id = hiddenIFrameID;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
iframe.src = url;
}
I found good answers here, but also found a simpler way.
The button to create the blob and the download link can be combined in one link, as the link element can have an onclick attribute. (The reverse seems not possible, adding a href to a button does not work.)
You can style the link as a button using bootstrap, which is still pure javascript, except for styling.
Combining the button and the download link also reduces code, as fewer of those ugly getElementById calls are needed.
This example needs only one button click to create the text-blob and download it:
<a id="a_btn_writetofile" download="info.txt" href="#" class="btn btn-primary"
onclick="exportFile('This is some dummy data.\nAnd some more dummy data.\n', 'a_btn_writetofile')"
>
Write To File
</a>
<script>
// URL pointing to the Blob with the file contents
var objUrl = null;
// create the blob with file content, and attach the URL to the downloadlink;
// NB: link must have the download attribute
// this method can go to your library
function exportFile(fileContent, downloadLinkId) {
// revoke the old object URL to avoid memory leaks.
if (objUrl !== null) {
window.URL.revokeObjectURL(objUrl);
}
// create the object that contains the file data and that can be referred to with a URL
var data = new Blob([fileContent], { type: 'text/plain' });
objUrl = window.URL.createObjectURL(data);
// attach the object to the download link (styled as button)
var downloadLinkButton = document.getElementById(downloadLinkId);
downloadLinkButton.href = objUrl;
};
</script>
Here is a single-page local-file version for use when you need the extra processing functionality of a scripting language.
Save the code below to a text file
Change the file extension from '.txt' to '.html'
Right-click > Open With... > notepad
Program word processing as needed, then save
Double-click html file to open in default browser
Result will be previewed in the black box, click download to get the resulting text file
Code:
<!DOCTYPE HTML>
<HTML>
<HEAD>
</HEAD>
<BODY>
<SCRIPT>
// do text manipulation here
let string1 = 'test\r\n';
let string2 = 'export.';
// assemble final string
const finalText = string1 + string2;
// convert to blob
const data = new Blob([finalText], {type: 'text/plain'});
// create file link
const link = document.createElement('a');
link.innerHTML = 'download';
link.setAttribute('download', 'data.txt');
link.href = window.URL.createObjectURL(data);
document.body.appendChild(link);
// preview the output in a paragraph
const htmlBreak = string => {
return string.replace(/(?:\r\n|\r|\n)/g, '<br>');
}
const preview = document.createElement('p');
preview.innerHTML = htmlBreak(finalText);
preview.style.border = "1px solid black";
document.body.appendChild(preview);
</SCRIPT>
</BODY>
</HTML>

WebRTC issue with File transfer in production application

Hello I am developing a WebRTC based file transfer application.I have deployed the application online using Heroku. But there seems to be a problem with file transfer specifically at the the receiving end which I have been unable to figure out. The file is transferred and received successfully when done on localhost but there is a problem with file reception when done in production. The browser used is Google Chrome if that's any help.
Here is the File reception code:
function dataChannelStateChanged() {
if (dataChannel.readyState === 'open') {
console.log("Data Channel open");
dataChannel.onmessage = receiveDataChannelMessage;
}
}
function receiveDataChannel(event) {
console.log("Receiving a data channel");
dataChannel = event.channel;
dataChannel.onmessage = receiveDataChannelMessage;
}
function receiveDataChannelMessage(event) {
console.log("From DataChannel: " + event.data);
if (fileTransferring) {
//Now here is the file handling code:
fileBuffer.push(event.data);
fileSize += event.data.byteLength;
fileProgress.value = fileSize;
//Provide link to downloadable file when complete
if (fileSize === receivedFileSize) {
var received = new window.Blob(fileBuffer);
fileBuffer = [];
downloadLink.href = URL.createObjectURL(received);
downloadLink.download = receivedFileName;
downloadLink.appendChild(document.createTextNode(receivedFileName + "(" + fileSize + ") bytes"));
fileTransferring = false;
//Also put the file in the text chat area
var linkTag = document.createElement('a');
linkTag.href = URL.createObjectURL(received);
linkTag.download = receivedFileName;
linkTag.appendChild(document.createTextNode(receivedFileName));
var div = document.createElement('div');
div.className = 'message-out';
div.appendChild(linkTag);
messageHolder.appendChild(div);
}
}
else {
appendChatMessage(event.data, 'message-out');
}
}
The following image shows the problem i'm encountering when receiving the file:
Any guidance would be much appreciated.
WEBRTC is based on UDP, it means there is no guarantee that your sequence of data transfers "in order" or successfully
use "webSocket" or a simple "http request" instead.
=============
UPDATE: see the first comment

How to solve Uncaught RangeError when download large size json

I am trying to download the large json data.
But it leads to Uncaught RangeError: Invalid string length.
Pls help to solve this problem,Thanks in advance.
Here is the Jsfiddle : http://jsfiddle.net/sLq3F/456/
You can use fetch(), Response.body.getReader() which returns a ReadableStream, TextDecoder(), Blob(), URL.createObjectURL().
Note, at device having limited RAM or low available disk space, after clicking Save at Save File dialog four minutes and twenty seconds 4:20 elapsed before the Save File dialog closed, followed by an additional one minute and thirty seconds 1:30 before the .crdownload extension was removed from the file at file manager GUI. During the first 4:20 period where the file is downloading to filesystem and the Save File dialog is visible the mouse pointer is movable, though the UI is temporarily unresponsive to clicks or attempts to change tabs. When the Save File dialog closes and the the file is still downloading to filesystem, having extension .crdownload the UI returns to normal functionality.
At the conclusion of the process described above the file was successfully downloaded to local filesystem having a total size of 189.8 MB (189,778,220 bytes).
<!DOCTYPE html>
<html>
<head>
<style>
code {
color:navy;
background-color:#eee;
padding:2px;
}
</style>
</head>
<body>
<button>Request File</button><br>
<progress min="0" max="189778220" value="0"></progress>
<output></output>
<br><br>
<label></label>
<script>
var url = "https://raw.githubusercontent.com/zemirco/sf-city-lots-json/master/citylots.json";
var button = document.querySelector("button");
var progress = document.querySelector("progress");
var label = document.querySelector("label");
var output = document.querySelector("output");
var request = (url) => {
label.innerHTML = `Requesting <code>${url}</code> at ${new Date()}.<br><br>`;
return fetch(url)
.then(response => response.body.getReader())
.then(reader => {
var decoder = new TextDecoder();
var json = "";
label.innerHTML += `Request successful.<br><br>Reading request body at ${new Date()}.<br><br>`;
return reader.read().then(function processData(result) {
if (result.done) {
// do stuff when `reader` is `closed`
return reader.closed.then(function() {
// return `json` string
return json;
});
};
json += decoder.decode(result.value);
output.innerHTML = ` ${json.length} of ${progress.max} bytes read`;
progress.value = json.length;
return reader.read().then(processData)
})
.then(function(data) {
var message = `Reading of <code>${url}</code> complete at ${new Date()}. <br><br>`
+ `${data.length} total bytes read. `
+ `Please allow up to 4 minutes for file to download `
+ `to filesystem after clicking <code>Save</code>.<br><br>`;
label.innerHTML += message;
var blob = new Blob([data], {
type: "application/json"
});
var file = URL.createObjectURL(blob);
var a = document.createElement("a");
a.download = "citylots.json";
a.href = file;
document.body.appendChild(a);
a.click();
var closeBlob = (e) => {
window.removeEventListener("focus", closeBlob);
blob.close();
URL.revokeObjectURL(file);
};
window.addEventListener("focus", closeBlob);
return message.replace(/<[^>]*>/g, "");
})
.catch(function(err) {
console.log("err", err)
})
});
}
var handleRequest = (e) => {
button.setAttribute("disabled", "disabled");
request(url).then(function(message) {
console.log(message);
button.removeAttribute("disabled");
})
};
button.addEventListener("click", handleRequest);
</script>
</body>
</html>
plnkr https://plnkr.co/edit/gewixzHZSKRXquZ2OVF2?p=preview
I guess you would need to loop through something in the JSON file and split it up into more manageable strings.
Tom
Do you have an example snippet the JSON file?
Do you have a server running on PHP?
if you do, I believe you must absolutely check your PHP.ini file or run a phpinfo page because php settings does limit the size of files transfer even if it isnt an external request. It could do that in other languages but I never had this problem except on PHP.
P.S. I did not see the size of the file
I think your json file is one of the "TOO BIG FOR JSON" scenarios. you know, if json file have so many records ( to be exact, based on a test, 100000 records make the browser hang and more then that failed to load in many browsers) its not recommend to use.
you can read this article for more information : HOW BIG IS TOO BIG FOR JSON?

How to upload base64 image resource with dropzone?

I'm trying to upload generated client side documents (images for the moment) with Dropzone.js.
// .../init.js
var myDropzone = new Dropzone("form.dropzone", {
autoProcessQueue: true
});
Once the client have finished his job, he just have to click a save button which call the save function :
// .../save.js
function save(myDocument) {
var file = {
name: 'Test',
src: myDocument,
};
console.log(myDocument);
myDropzone.addFile(file);
}
The console.log() correctly return me the content of my document
data:image/png;base64,iVBORw0KGgoAAAANS...
At this point, we can see the progress bar uploading the document in the drop zone but the upload failed.
Here is my (standart dropzone) HTML form :
<form action="/upload" enctype="multipart/form-data" method="post" class="dropzone">
<div class="dz-default dz-message"><span>Drop files here to upload</span></div>
<div class="fallback">
<input name="file" type="file" />
</div>
</form>
I got a Symfony2 controller who receive the post request.
// Get request
$request = $this->get('request');
// Get files
$files = $request->files;
// Upload
$do = $service->upload($files);
Uploading from the dropzone (by drag and drop or click) is working and the uploads are successfull but using the myDropzone.addFile() function return me an empty object in my controller :
var_dump($files);
return
object(Symfony\Component\HttpFoundation\FileBag)#11 (1) {
["parameters":protected]=>
array(0) {
}
}
I think i don't setup correctly my var file in the save function.
I tryied to create JS image (var img = new Image() ...) but without any success.
Thanks for your help !
Finally i found a working solution without creating canvas :
function dataURItoBlob(dataURI) {
'use strict'
var byteString,
mimestring
if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
byteString = atob(dataURI.split(',')[1])
} else {
byteString = decodeURI(dataURI.split(',')[1])
}
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]
var content = new Array();
for (var i = 0; i < byteString.length; i++) {
content[i] = byteString.charCodeAt(i)
}
return new Blob([new Uint8Array(content)], {type: mimestring});
}
And the save function :
function save(dataURI) {
var blob = dataURItoBlob(dataURI);
myDropzone.addFile(blob);
}
The file appears correctly in dropzone and is successfully uploaded.
I still have to work on the filename (my document is named "blob").
The dataURItoBlob function have been found here : Convert Data URI to File then append to FormData
[EDIT] : I finally wrote the function in dropzone to do this job. You can check it here : https://github.com/CasperArGh/dropzone
And you can use it like this :
var dataURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmAAAAKwCAYAAA...';
myDropzone.addBlob(dataURI, 'test.png');
I can't comment currently and wanted to send this to you.
I know you found your answer, but I had some trouble using your Git code and reshaped it a little for my needs, but I am about 100% positive this will work for EVERY possible need to add a file or a blob or anything and be able to apply a name to it.
Dropzone.prototype.addFileName = function(file, name) {
file.name = name;
file.upload = {
progress: 0,
total: file.size,
bytesSent: 0
};
this.files.push(file);
file.status = Dropzone.ADDED;
this.emit("addedfile", file);
this._enqueueThumbnail(file);
return this.accept(file, (function(_this) {
return function(error) {
if (error) {
file.accepted = false;
_this._errorProcessing([file], error);
} else {
file.accepted = true;
if (_this.options.autoQueue) {
_this.enqueueFile(file);
}
}
return _this._updateMaxFilesReachedClass();
};
})(this));
};
If this is added to dropzone.js (I did just below the line with Dropzone.prototype.addFile = function(file) { potentially line 1110.
Works like a charm and used just the same as any other. myDropzone.addFileName(file,name)!
Hopefully someone finds this useful and doesn't need to recreate it!
1) You say that: "Once the client have finished his job, he just have to click a save button which call the save function:"
This implies that you set autoProcessQueue: false and intercept the button click, to execute the saveFile() function.
$("#submitButton").click(function(e) {
// let the event not bubble up
e.preventDefault();
e.stopPropagation();
// process the uploads
myDropzone.processQueue();
});
2) check form action
Check that your form action="/upload" is routed correctly to your SF controller & action.
3) Example Code
You may find a full example over at the official Wiki
4) Ok, thanks to your comments, i understood the question better:
"How can i save my base64 image resource with dropzone?"
You need to embedd the image content as value
// base64 data
var dataURL = canvas.toDataURL();
// insert the data into the form
document.getElementById('image').value = canvas.toDataURL('image/png');
//or jQ: $('#img').val(canvas.toDataURL("image/png"));
// trigger submit of the form
document.forms["form1"].submit();
You might run into trouble doing this and might need to set the "origin-clean" flag to "true". see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#security-with-canvas-elements
how to save html5 canvas to server

How can JavaScript save to a local file?

There's already a solution for writing file JSON online but I want to save json file locally.
I've tried to use this example http://jsfiddle.net/RZBbY/10/
It creates a link to download the file, using this call
a.attr('href', 'data:application/x-json;base64,' + btoa(t.val())).show();
Is there a way to save the file locally instead of providing a downloadable link?
There are other types of conversion beyond data:application/x-json;base64?
Here's my code:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>jQuery UI Sortable - Default functionality</title>
<link rel="stylesheet" href="http://jqueryui.com/themes/base/jquery.ui.all.css">
<script src="http://jqueryui.com//jquery-1.7.2.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.core.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.widget.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.mouse.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.sortable.js"></script>
<script src="http://jqueryui.com/ui/jquery.ui.accordion.js"></script>
<link rel="stylesheet" href="http://jqueryui.com/demos/demos.css">
<meta charset="utf-8">
<style>a { font: 12px Arial; color: #ac9095; }</style>
<script type='text/javascript'>
$(document).ready(function() {
var f = $('form'), a = $('a'),
i = $('input'), t = $('textarea');
$('#salva').click(function() {
var o = {}, v = t.val();
a.hide();//nasconde il contenuto
i.each(function() {
o[this.name] = $(this).val(); });
if (v === '') {
t.val("[\n " + JSON.stringify(o) + " \n]")
}
else {
t.val(v.substr(0, v.length - 3));
t.val(t.val() + ",\n " + JSON.stringify(o) + " \n]")
}
});
});
$('#esporta').bind('click', function() {
a.attr('href', 'data:application/x-json;base64,' + btoa(t.val())).show();
});
</script>
</head>
<body>
<form>
<label>Nome</label> <input type="text" name="nome"><br />
<label>Cognome</label> <input type="text" name="cognome">
<button type="button" id="salva">Salva</button>
</form>
<textarea rows="10" cols="60"></textarea><br />
<button type="button" id="esporta">Esporta dati</button>
Scarica Dati
</body>
</html>
Based on http://html5-demos.appspot.com/static/a.download.html:
var fileContent = "My epic novel that I don't want to lose.";
var bb = new Blob([fileContent ], { type: 'text/plain' });
var a = document.createElement('a');
a.download = 'download.txt';
a.href = window.URL.createObjectURL(bb);
a.click();
Modified the original fiddle: http://jsfiddle.net/9av2mfjx/
You should check the download attribute and the window.URL method because the download attribute doesn't seem to like data URI.
This example by Google is pretty much what you are trying to do.
It is not possible to save file locally without involving the local client (browser machine) as I could be a great threat to client machine. You can use link to download that file. If you want to store something like Json data on local machine you can use LocalStorage provided by the browsers, Web Storage
The possible ways to create and save files in Javascript are:
Use a library called FileSaver
saveAs(new File(["CONTENT"], "demo.txt", {type: "text/plain;charset=utf-8"}));
Create a blob object and offer a “save as”.
var a = document.createElement("a");
a.href = window.URL.createObjectURL(new Blob(["CONTENT"], {type: "text/plain"}));
a.download = "demo.txt";
a.click();
Upload the data, save it on the server.
var data = new FormData();
data.append("upfile", new Blob(["CONTENT"], {type: "text/plain"}));
fetch("SERVER.SCRIPT", { method: "POST", body: data });
Create a writable file stream.
const fileHandle = await window.showSaveFilePicker();
const fileStream = await fileHandle.createWritable();
await fileStream.write(new Blob(["CONTENT"], {type: "text/plain"}));
await fileStream.close();
In NodeJS, simply use the file system module
require("fs").writeFile("demo.txt", "Foo bar!");
<!-- (A) LOAD FILE SAVER -->
<!-- https://cdnjs.com/libraries/FileSaver.js -->
<!-- https://github.com/eligrey/FileSaver.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script>
// (B) "SAVE AS"
var myFile = new File(["CONTENT"], "demo.txt", {type: "text/plain;charset=utf-8"});
saveAs(myFile);
</script>
// (A) CREATE BLOB OBJECT
var myBlob = new Blob(["CONTENT"], {type: "text/plain"});
// (B) CREATE DOWNLOAD LINK
var url = window.URL.createObjectURL(myBlob);
var anchor = document.createElement("a");
anchor.href = url;
anchor.download = "demo.txt";
// (C) "FORCE DOWNLOAD"
// NOTE: MAY NOT ALWAYS WORK DUE TO BROWSER SECURITY
// BETTER TO LET USERS CLICK ON THEIR OWN
anchor.click();
window.URL.revokeObjectURL(url);
document.removeChild(anchor);
<script>
function blobajax () {
// (A) CREATE BLOB OBJECT
var myBlob = new Blob(["CONTENT"], {type: "text/plain"});
// (B) FORM DATA
var data = new FormData();
data.append("upfile", myBlob);
// (C) AJAX UPLOAD TO SERVER
fetch("3b-upload.php", {
method: "POST",
body: data
})
.then((res) => { return res.text(); })
.then((txt) => { console.log(txt); });
}
</script>
<input type="button" value="Go" onclick="blobajax()"/>
<script>
async function saveFile() {
// (A) CREATE BLOB OBJECT
var myBlob = new Blob(["CONTENT"], {type: "text/plain"});
// (B) FILE HANDLER & FILE STREAM
const fileHandle = await window.showSaveFilePicker({
types: [{
description: "Text file",
accept: {"text/plain": [".txt"]}
}]
});
const fileStream = await fileHandle.createWritable();
// (C) WRITE FILE
await fileStream.write(myBlob);
await fileStream.close();
}
</script>
<input type="button" value="Save File" onclick="saveFile()"/>
// (A) LOAD FILE SYSTEM MODULE
// https://nodejs.org/api/fs.html
const fs = require("fs");
// (B) WRITE TO FILE
fs.writeFile("demo.txt", "CONTENT", "utf8", (error, data) => {
console.log("Write complete");
console.log(error);
console.log(data);
});
/* (C) READ FROM FILE
fs.readFile("demo.txt", "utf8", (error, data) => {
console.log("Read complete");
console.log(error);
console.log(data);
});
*/
It all depends on what you are trying to achieve with "saving locally". Do you want to allow the user to download the file? then <a download> is the way to go. Do you want to save it locally, so you can restore your application state? Then you might want to look into the various options of WebStorage. Specifically localStorage or IndexedDB. The FilesystemAPI allows you to create local virtual file systems you can store arbitrary data in.
While most despise Flash, it is a viable option for providing "save" and "save as" functionality in your html/javascript environment.
I've created a widget called "OpenSave" that provides this functionality available here:
http://www.gieson.com/Library/projects/utilities/opensave/
-mike
So, your real question is: "How can JavaScript save to a local file?"
Take a look at http://www.tiddlywiki.com/
They save their HTML page locally after you have "changed" it internally.
[ UPDATE 2016.01.31 ]
TiddlyWiki original version saved directly. It was quite nice, and saved to a configurable backup directory with the timestamp as part of the backup filename.
TiddlyWiki current version just downloads it as any file download. You need to do your own backup management. :(
[ END OF UPDATE
The trick is, you have to open the page as file:// not as http:// to be able to save locally.
The security on your browser will not let you save to _someone_else's_ local system, only to your own, and even then it isn't trivial.
-Jesse
If you are using FireFox you can use the File HandleAPI
https://developer.mozilla.org/en-US/docs/Web/API/File_Handle_API
I had just tested it out and it works!

Categories

Resources