How to store file in a browser using JavaScript - javascript

I am working for a prototype of website(Only Browser based). There is a part where I need to upload some files.Here I am using JavaScript and HTML.
Whenever user wants to upload(Like Browse button in applications) some files then it will available for next time.I am unable to do this.
Question Can we save/store/upload a file using JavaScript/HTML in browser only(Not server )??
Thanks

Downloading file directly to user's file-system
If you by save/store mean directly to user's computer/file system then no, this is not possible due to security reasons.
However, you can initiate a download which will pop up a "save as" type of requester and allow user to accept or deny to download the file.
There are more than one way to initiate a download. An interesting one is the new download attribute for the anchor tag where you can specify a file name as well as automatically initiate the download setting its href to a data-uri or something else you want to reference for download:
Click to download
Local storage mechanisms
If you simply want to save the file locally you can use one of the many local storage mechanisms such as:
File system API (only supported in Chrome currently and the new Opera. In draft status)
Indexed DB (allows Blob objects. Good support)
Web SQL (deprecated but widely in use)
Web Storage (very good support but only stores strings and has limited space, objects can be saved as JSON strings)
Note that all of these as sand-boxed and only available in the browser using the same origin as they was written from. The data may or may not be discarded at any point as well (by user or by browser) so they are not a "safe" storage (always keep a server copy or a way to regenerate the data).

Yes, it's possible via FileSystem API (currently only Chrome and Opera).
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
document.querySelector('input[type="file"]').onchange = function(e) {
var files = this.files;
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
// Duplicate each file the user selected to the app's fs.
for (var i = 0, file; file = files[i]; ++i) {
// Capture current iteration's file in local scope for the getFile() callback.
(function(f) {
fs.root.getFile(f.name, {create: true, exclusive: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.write(f); // Note: write() can take a File or Blob object.
}, errorHandler);
}, errorHandler);
})(file);
}
}, errorHandler);
};

"Can we save/store/upload a file using JavaScript/HTML in browser only(Not server )??"
Ans. is No. If you want to retain the uploaded file then you need to store it on server.
The moment the browser tab is closed the file will get lost.
Alternative: What you can do is store the name of the file on server and whenever user request the file then ask him to upload it.
For uploading the file into memory you can refer to this link .
Live Demo

Related

Write to a local JSON file from the browser [duplicate]

This question already has answers here:
Download JSON object as a file from browser
(14 answers)
Closed 7 months ago.
I'm trying to write to my local JSON file with javascript, I'm getting it from my HTML, changing it and want to write it back. I found how to read it from here but didn't find how to write to it back to the json file.
Note: I want to this without Node.js , 'fs' won't help..
My code to get the JSON file:
<script type="text/javascript" src="data.json></script>
<script type="text/javascript" src="javascrip.js"></script>
var mydata = JSON.parse(data);
Then I changed the value of the 'name', for example.
mydata["name"] = "NewName";
And then I want to send it back to the json file, update it.
I didn't really find any code to do so.
While you can't directly write to the file system due because of security constraints, you can trigger a save dialog to request the user to save the file. I use this function which works on all recent releases of the major browsers.
function download(data, filename) {
// data is the string type, that contains the contents of the file.
// filename is the default file name, some browsers allow the user to change this during the save dialog.
// Note that we use octet/stream as the mimetype
// this is to prevent some browsers from displaying the
// contents in another browser tab instead of downloading the file
var blob = new Blob([data], {type:'octet/stream'});
//IE 10+
if (window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob, filename);
}
else {
//Everything else
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
document.body.appendChild(a);
a.href = url;
a.download = filename;
setTimeout(() => {
//setTimeout hack is required for older versions of Safari
a.click();
//Cleanup
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}, 1);
}
}
It is also worth mentioning that HTML5 spec has a download attribute, but it isn't currently supported on IE.
As ArtemSky said, You can't write to the local file system. The way to accomplish what you want to do would be to use a server that can write to it's local file system or a database or whatever.
So you would want to have the data stored somewhere, either a local file on the server, in the cloud, etc. or a database of some sort. Then you would set up an API on the server that you could call remotely to get the data via an XMLHttpRequest(XHR)
Then you would create another API method you can use to send the data back and then call that with the updated/new data.
Writing to the local file system is a security concern because if anyone who can write code could overwrite your system files otherwise. Preventing the ability to write to the local file system is the only way to make the web safe.
You can't write to file system due to security constraints of the browser.
You can do that only this way - https://stackoverflow.com/a/30800715/6149665

Js - Cache file, open it, detect changes and upload

I have a file structure on a web page, and look for a solution for the following scenario:
The chosen file should be downloaded in browser cache and opened (if it's an excel document, open with excel, etc.).
Now when the user changes the file, it should be detected and the file should be uploaded again.
Is this even possible with JavaScript?
If yes, where do I store the file (temporary internet folder?) and how do I detect the changes?
The only way for this to work you would need to have the user select the downloaded file, and then check for modification.
HTML
<label for="excelFile">Select the excel file: </label><input type="file" id="excelFile" />
JS
//Change event to detect when the user has selected a file
document.querySelector("#excelFile").addEventListener("change",function(e){
//get the selected file
var file = this.files[0];
//get the last modified date
var lastModified = file.lastModified;
//check lastModified against stored lastModified
//this assumes you store the last mod in localStorage
if(localStorage['excelLastMod'] < lastModified){
//It has modified update last mod
localStorage['excelLastMod'] = lastModified;
//do upload
}
});
If you know your user is using Chrome you can use Chrome's FileSystem api
The way you describe it: No, that is not possible in JavaScript.
It sounds like you want an FTP client.
When the user changes the file, it should be detected and the file should be uploaded again.
That is not possible due to JS having almost no access to the file system.
The only way you can access a file at all is by requesting the user to select one, see:
How to open a local disk file with Javascript?
So the most you could do would be:
File is downloaded.
Based on browser & settings, file may be opened automatically, or not.
User is presented with a file selection dialog that they can use when they are done editing.
Compare selected file to file on server and upload if changed.
After downloading a file, you have no control over it.
For applications that have a protocol registered (such a steam://, for example), you might be able to request the URL being opened in a program, but that would require an if per file type/program.
Detecting file changes is not at all possible (because you have no access to the file), and uploading again requires the user to select the file manually, using a file dialog.
Thanks for your help and ideas. I saw a software (https://www.group-office.com/) which includes this function so there has to be way to do it.
New Idea, using chrome filesystem api (#Siguza already said it):
Create file from servers database on users local filesystem with filesystem api
open file locally (should work with filesystem:http://www.example.com/persistent/info.txt, or?)
poll last changes of file every x seconds
if change detected, upload file back to servers database
I saw some problems with excel locking the files Check if file has changed using HTML5 File API
but except of that this should work, shouldn't it?

Is it possible to stream an octet stream being generated in javascript?

Lets suppose a case where a huge string is generated from a small string using some javascript logic, and then the text file is forced to be downloaded on the browser.
This is possible using an octet-stream download by putting it as an href , as mentioned in this answer :
Create a file in memory for user to download, not through server.
function download(filename, text) {
var pom = document.createElement('a');
pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
pom.setAttribute('download', filename);
pom.click();
}
But this solution requires 'text' to be fully generated before being pushed for the download,
hence it will have to be held in browser memory fully .
Is it possible to stream the text as it gets generated using CLIENT SIDE LOGIC ONLY ?
For example :
var inputString = "A";
var outStr = "";
for(var i = 0; i < 10000000 ; i++)
{
/* concatenate inputString to output on the go */
}
Yes & no. No because there's not a way to write to files with just client-side javascript. Kinda. You can prompt a user to download & save a file, but as you mentioned, the code must generate the whole file before that download happens. Note: By "stream" I assume you mean stream to file (constantly write to a file) & by "CLIENT SIDE LOGIC ONLY" I assume you mean in the browser.
Looks like Mozilla has been working on a way to let client-side code interact with files. Here comes the yes. Kind of. They have their own file system api that lets you interact with (write to) the local machines file system. Specifically, there's a function that lets you write an input stream to a file. However, there's a few asterisks:
1) looks like that whole system is being deprecated; they encourage developers to use OS.file over File I/O
2) You have to use XPConnect, a system that lets you access Mozilla's XPCOM (component library) in javascript. If you want to do this in the browser, it looks like only firefox extensions have the proper permissions to interact with those components (). If you didn't want to do this in the browser, you obviously could just use node.
Assuredly, more complications are bound to show up during implementation. But this looks like the most sure path forward, seeing as how OS.File gives you access to functions like OS.File.writeAtomic() & basic write to file
That being said, it's not that great of a path, but hopefully this gives you a solid starting point. As #dandavis mentioned, browsers (i.e. "client side logic") are designed to not allow this sort of thing. It would be an incredibly huge oversight / security flaw if a website could interact with any user's local file system.
Additional resources:
Wikipedia on XPConnect
Guide on working with XPCOM in javascript - may not be that useful
There is a way to do this, but it relies on a Chrome only Filesystem API. We will create and write to a temporary file in a sandboxed file system and the copy it to the regular file system once we are done. This way you do not have to store the entire file in memory. The asynchronous version of the Chrome API is not currently being considered for standardization by W3C, but the synchronous verison (which uses web workers) is. If browser support is a concern, then this answer is not for you.
The API works like this:
First, we get the requestFileSystem() function from the browser. Currently it is prefixed by "webkit":
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
Next, we request a temporary file system (this way we do not need to ask for user permission):
var fileSystem; //This will store the fileSystem for later access
var fileSize = 1024*1024 //Our maximum file system size.
function errorHandler(e) {
console.log('Error: ' + e.name);
}
window.requestFileSystem(window.TEMPORARY, fileSize, function (fs) { fileSystem = fs; }, errorHandler);
Now that we have access to the file system it is time to create a file:
var fileOptions = {
create: true, //If the file is not found, create it
exclusive: false //Don't throw an error if the file doesn't exist
};
Here we call the getFile() function, which can create a file if it doesn't exist. Inside of the callback, we can create a new fileWriter for writing to the file. The fileWriter is then moved to the end of the file, and we create a new text blob to append to it.
fileSystem.root.getFile(fileName, fileOptions, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.seek(fileWriter.length);
var blob = new Blob([STRING_TO_WRITE], {type: 'text/plain'});
fileWriter.write(blob);
}, errorHandler);
});
Note that this API does not save to the normal, user filesystem. Instead, it saves to a special sandboxed folder. If you want to save it to the user's file system, you can create a filesystem: link. When the user clicks on it, it will prompt them to save it. After they save it, you can then remove the temporary file.
This function generates the filesystem link using the fileEntry's toURL() function:
var save = function () {
var download = document.querySelector("a[download]");
if (!fileSystem) { return; }
fileSystem.root.getFile(fileName, {create: false, exclusive: true}, function(fileEntry) {
download.href = fileEntry.toURL();
}, errorHandler);
}
Using a link with the download attribute will force the download of the file.
<a download></a>
Here is a plunker that demonstrates this: http://plnkr.co/edit/q6ihXWEXSOtutbEy1b5G?p=preview
Hopefully this accomplishes what you want. You can continuously append to the file, it won't be kept in memory, but it will be in the sandboxed filesystem until the user saves it to the regular filesystem.
For more information take a look at this HTML5rocks article or this one if you want to use the newer, synchronous Web Worker API.
I would have suggest it the way #quantumwannabe describes it, using temporary sandbox file to append chunks.
But there is a new way that can be used today (behind a flag) but will be enabled in the next version of chrome (52)
And here is where i will make #KeenanLidral-Porter answer incorrect. And #quantumwannabe answer a unnecessary step
Because there is now a way to write a stream to the filesystem directly: StreamSaver.js
It acts as if there was a server sending octet-stream header and tells the browser to download chunks of data with help of a service worker
const writeStream = streamSaver.createWriteStream('filename.txt')
const encoder = new TextEncoder
let data = 'a'.repeat(1024) // Writing some stuff triggers the save dialog to show
let uint8array = encoder.encode(data + "\n\n")
writeStream.write(uint8array) // Write some data when you got some
writeStream.close() // End the saving

HTML5 FileSystem API

I have created file 'log.txt' by fileSystem API
function initFS(grantedBytes) {
window.requestFileSystem(window.PERSISTENT, grantedBytes, function (filesystem) {
fs = filesystem;
fs.root.getFile('log.txt', { create: true, exclusive: true }, function (fileEntry) {
// fileEntry.isFile === true
// fileEntry.name == 'log.txt'
// fileEntry.fullPath == '/log.txt'
console.log(fileEntry.fullPath);
}, errorHandler);
}, errorHandler);
}
initFS(1024*1024);
And do not fully understand its structure. Is There any way to explore this file
for example from Windows Explorer and see it in file system?
There is an even simpler way. On chrome, visit these urls.
For http, it's "filesystem:http://"+location.host+"/persistent/".
For https, it's "filesystem:https://"+location.host+"/persistent/".
Sort of, the File-system API doesn't encrypt the data being stored locally. It does however change the file naming conventions up. So you may have named it log.txt but if you poke around where the file-system API stores files, you'd probably find it under some arbitrary randomly generated file name like "00010" or in a random directory like "24/00123".
Anyway, you can open each file up in a text editor - if your file had text written to it you would be able to view it as such. Or if you wrote JSON to the file-system API it would be in human-readable string format when you opened in the text editor.
On Windows 7, with Chrome it's found here:
C:\Users\{user}\AppData\Local\Google\Chrome\User Data\Default\File System\
If you want to find out where it is stored via Chrome on other OS please see this post
Log files that an end-user or maintainer might want to see should be stored someplace in the normal file system. While the checked answer suggests how to find them when the HTML5 API is used, this location is subject to change and is troublesome to find.
A better solution is to have the user choose the directory for log files (and perhaps other files) when the app is installed, using chrome.fileSystem.chooseEntry, and then retain that entry and save it in local storage so it can be reused on subsequent launches.

How to save the window.URL.createObjectURL() result for future use?

I'm making an application in HTML5 where you choose a video file, and then the application plays it with the HTML5 video tag and the window.URL.createObjectURL(). The problem is that I want to save the data about this video in localStorage and play it again when the user uses my application, but as Mozilla MDN states about the results of this method:
Browsers will release these automatically when the document is unloaded
So is it possible to do what I'm trying to do? Or do the same thing without the window.URL.createObjectURL() but with something else?
I haven't used createObjectURL(), but if I understand correctly, it's essentially a temporary reference to a file or an in-memory object. If you want to save the actual video, it won't be useful, because the video itself will no longer be referenced by this pointer the next time the user visits the application.
I think you might be able to do this with a data: URL instead, as that URL actually includes the full data from the file. This example demonstrates using a FileReader to generate a data URL. I think you should be able to do this:
var reader = new FileReader();
reader.onload = function(e) {
var myDataUrl = e.target.result;
// do something with the URL in the DOM,
// then save it to local storage
};
reader.readAsDataURL(file);
Update: If you want to go up to 1GB, as you note in your comment, you'd probably be better served by the FileSystem API. This would require you to get the local file, save a copy of the file to persistent filesystem storage, and then use createObjectURL() to get a URL for the file copy. You still have a problem with disk space - you just added 1GB of duplicative file content to the user's filesystem - but I don't think it's possible to keep a persistent reference to a file outside of the browser sandbox otherwise.

Categories