I'm developing a chrome extension and I would like to store some data in a file permanently in the users system so that I can retrieve that data every time the user runs the extension.
Is there a way I can accomplish this with the chrome javascript API?
Thanks in advance.
From your description, it sounds like you would be better off using the Chrome Storage API, which writes data into a browser-managed database. It is kept across sessions and browser-restarts.
chrome.storage.sync.set({'theKey': theValue}, optionalSuccessCallback);
Use any number of keys and any value as long as it is serializable. This is fast and especially useful for configuration settings and alike. Your extension needs to request permission:
"permissions": ["storage"]
If your really want to create files (e.g. a bunch of mp3's to use as actual files later), Anatoly is right, use webkitRequestFileSystem. It requires quite a few callbacks:
function onInitFs(fs) {
fs.root.getFile('log.txt', {create: true}, function(fileEntry) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function(e) {
console.log('Write completed.');
};
fileWriter.onerror = function(e) {
console.log('Write failed: ' + e.toString());
};
// Create a new Blob and write it to log.txt.
var blob = new Blob(['Lorem Ipsum'], {type: 'text/plain'});
fileWriter.write(blob);
}, errorHandler);
}, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
It's possible with webkitRequestFileSystem.
You'll be able to access sandboxed persistent folder to create and read files from.
Related
I have this code:
document.querySelector('#myfile').onchange = function(e) {
var files = this.files;
window.requestFileSystem(window.TEMPORARY, 1024 * 1024, function(fs) {
let file = files[0];
let nem_file_name = file.name + '_copy';
fs.root.getFile(nem_file_name, {
create: true,
exclusive: true
}, function(fileEntry) {
fileEntry.createWriter(fileWriter => {
fileWriter.write(file);
}, () => alert('error 1'));
}, err => alert('error 2 ' + err));
}, () => alert('error 3'));
};
<input type="file" id="myfile" ref="myfile" multiple />
I want to create a copy of my file when I select it with the input control. What's wrong with my code? I got no errors and nothing happens
The "modern" API is called File System Access, it is still only a proposal, but is expected to supersede the previous and deprecated File System API, and is already available in Chromium browsers.
To write a file using this API, you first request a FileHandle using the showSaveFilePicker() method, then you can create a writer from that handle and append data using that writer:
onclick = async (e) => { // needs to be initiated by an user gesture
const handle = await showSaveFilePicker(); // prompt "Save As"
const writer = await handle.createWritable(); // request writable stream
await writer.write( new Blob( [ "some data" ] ) ); // write the Blob directly
writer.close(); // end writing
};
But this API is still overprotected, so it unfortunately can't be ran in cross-origin iframes like the ones used here by StackSnippet or most popular fiddling services. So in order to make a live demo, I had to make a plunker, that you must run in windowed mode.
And if you need to set the name of the file yourself, you need to request for a directory access using showDirectoryPicker() and then to get a FileHandle from that directory handle using its getFileHandle() method. plnkr
The documentation states that this method is non-standard, only in Chrome and already deprecated:
Even compared to the rest of the File and Directory Entries API,
requestFileSystem() is especially non-standard; only Chrome implements
it, and all other browser makers have decided that they will not
implement it. It has even been removed from the proposed
specification. Do not use this method!
You should not use this.
Here is the function:
this.saveObj = function(o, finished)
{
root.getDirectory("object", {create: true}, function(directoryEntry)
{
directoryEntry.getFile("object.json", {create: true}, function(fileEntry)
{
fileEntry.createWriter(function(fileWriter)
{
fileWriter.onwriteend = function(e)
{
finished(fileEntry);
};
fileWriter.onerror = errorHandler;
var blob = new Blob([JSON.stringify(o)], {type: "json"});
fileWriter.write(blob);
}, errorHandler);
}, errorHandler);
}, errorHandler);
};
Now when I save an object everything works fine. Lets say I save {"id":1} my file content would be {"id":1}. Now I edit the object with o = {}; and save it again, my file content suddenly is {} "id":1 }.
It just overwrites the old content, but doesn't clean it. Do I have to delete the file before writing it or is there something I'm missing?
For as far as I understand the write method will write the supplied content to a position. To me this implies that the existing content is untouched unless you are overwriting parts. So I'm going to say yes, delete the file and save a new one.
source
According to the Mozilla documentation using only { create: true} :
The existing file or directory is removed and replaced with a new one,
then the successCallback is called with a FileSystemFileEntry or a
FileSystemDirectoryEntry, as appropriate.
Tested in Chrome 72 this seems to be the case.
This does not work as the file seems to be persist. The file will be overwritten (first bytes) but the size will remain the same. So this is a bug in at least Chrome 72.
Source
I'm using HTML5 FileWriter API to save the state of my webapp. I have bit of JS that periodically calls FileWriter.write to do that (so , over time, the write method is called several times). By default FileWriter API use an 'append' approach to writing files which does not suits my needs since I wan't to overwrite the file content.
I first tried this:
this._writer.seek(0);
this._writer.write(content);
This is not working when you are writing a text shorter than the file content. I then tried this:
this._writer.truncate(0);
this._writer.write(content);
This code is supposed to clear the file and then write my new content but I'm getting the following error when write method is called:
Uncaught InvalidStateError: An operation that depends on state cached in an interface object was made but the state had changed since it was read from disk.
Odd thing: when I debug the code (with a breakpoint), the error does not occur, as if FileWriter.truncate was an asynchronous method...
I am stuck here, any ideas?
I am using Chrome 30.0.1599.69
Here is a correct code that won't waste 500ms on waiting
fileWriter.onwriteend = function() {
if (fileWriter.length === 0) {
//fileWriter has been reset, write file
fileWriter.write(blob);
} else {
//file has been overwritten with blob
//use callback or resolve promise
}
};
fileWriter.truncate(0);
You can truncate and then write with two different FileWriter objects.
fileEntry.createWriter(function (fileWriter) {
fileWriter.truncate(0);
}, errorHandler);
fileEntry.createWriter(function (fileWriter) {
var blob = new Blob(["New text"], { type: 'text/plain' });
fileWriter.write(blob);
}, errorHandler);
If you want to always override it, you can use this method
function save(path,data){
window.resolveLocalFileSystemURL(dataDirectory, function(dir){
dir.getFile(path, {create:true}, function(file){
file.createWriter(function(fileWriter){
fileWriter.seek(0);
fileWriter.truncate(0);
var blob = new Blob([data], {type:'text/plain'});
fileWriter.write(blob);
}, function(e){
console.log(e);
});
});
});
};
A workaround is the following code:
this._writer.truncate(0);
window.setTimeout(function(){
this._writer.write(content);
}.bind(this),500)
This simply wait 500 milliseconds before writing. Not great but it works...
This is the simplest way how i use to delete the content of a file with syncFileSystem in my Chrome App.
Two createWriter, the first one truncates then the second one overwrites with nothing (you can change with your new value) :
file.createWriter((fileWriter)=>fileWriter.truncate(0));
file.createWriter((fileWriter)=> {
fileWriter.onwriteend = function() {
console.log('New Actions!');
};
var blob = new Blob([''], {type: 'text/plain'});
fileWriter.write(blob);
});
Chrome implements the file interface as described here http://www.html5rocks.com/en/tutorials/file/filesystem/, just adding the webkit prefix. The documentation covers several aspects of the interface, but what are the simplest steps, for example, to prompt the user with a file saving dialog, or to tell him that the file has been saved somewhere? For example, let's say we want to save some text data for the user.
I'm mainly referring to lines of code as a metric of simplicity, but within the 80 characters per line (and common sense). I'm also referring to Chrome 26.
This is what i found. Naturally, it's use is quite limited, and it is better to refer to the main article linked above
function error(e) { console.log(e); };
webkitRequestFileSystem(TEMPORARY, Math.pow(2, 10), function(fs) {
fs.root.getFile( 'exported.txt', {create:true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function() {
alert('content saved to '+fileEntry.fullPath);
};
var blob = new Blob(['Lorem Ipsum'], {type: 'text/plain'});
fileWriter.write(blob);
});
}, error);
}, error);
I'm working with a chrome extension to copy user selected files into the extension's filesystem. I'm not getting any errors, but when I try to view the image, it's broken. Here's an example of the code:
this.create = function(obj, attr, calling_model){
// Get parent directory
fs.root.getDirectory(obj.type, {create: true}, function(dirEntry) {
// Create file
dirEntry.getFile(obj.type+'-'+obj.id+'-'+attr.name, {create: true, exclusive: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.write(attr.value);
}, errorHandler);
alert('just wrote a file to: '+fileEntry.toURL());
// Update passed object
obj[attr.name] = fileEntry.toURL();
calling_model.update(obj);
}, errorHandler);
}, errorHandler);
};
where attr.value = the value of a file input. I feel like I should be turning the value of the file input into a blob before writing it to the filesystem, but I haven't found any examples of how to do that. Has anyone tackled this problem before?
Thanks in advance...
If attr.value is a File object, your code should work. I wrote a section on "duplicating user selected files" in my HTML5Rocks article on the Filesystem API that should help.