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
Related
Using a local Apache web server for testing, with Chrome (33) and a very basic piece of code
function onInitFs(fs) {
fs.root.getFile("productinfo.xml", {}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
.
.
.
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
No matter where I put the file (productinfo.xml), I get:
A requested file or directory could not be found at the time an operation was processed
My root directory is C:\xampp\htdocs so putting productinfo.xml there should work?
As the comments pointed out, you're going to want to make an AJAX call - you don't obtain the file without grabbing it from the server. I'm not certain if you are going to just stick with making the AJAX call everytime. However, working with the HTML5-File system can keep you from re-grabbing the XML every-time.
The code/my answer below is to grab the file locally when it exists and grab it from the server when it doesn't exist locally your code would look like the following (or something very similar - I copy and pasted a lot of working code and tried to abstract some components):
Function call to get the xml file,
whether it's locally or from the server, see code below - make sure fs is initialized before making the following call, this is done by setting it to a global variable in your onInitFs called in the request File system function
getFile("productinfo.xml",function(textDataFromFile){
console.log("some callback function"}
//your ... code should be handled here
);
the AJAX call to get your file from the server
function obtainFileFromServer(filename,callback){
var xhr2 = new XMLHttpRequest();
xhr2.onload = function(e){
writeToFile(filename,xhr2.response,callback);
}
xhr2.open('GET', "path/to/"+filename, true);
xhr2.send();
}
Reading from the HTML5-File system.
function getFile(filename,callback){
fs.root.getFile(filename, {create:false}, function(fileEntry) {
fileEntry.file(function(file) {
var errorHandler2 = function(e){
switch(e.name){
case "NotFoundError":
//if the productinfo.xml is not found, then go get it from the server
//In you're callback you'll want to also recall the getFile
obtainFileFromServer(function(){getFile(filename,callback);});
break;
default:
console.log(e);
break;
}
}
var reader = new FileReader();
reader.onloadend = function(e) {
callback(this.result);
};
reader.readAsText(file);
}, errorHandler2);
}, errorHandler);
}
Writing to HTML5 File-system
There are two ways you can look at the write method (writeToFile()), in the event you are replacing an old file, and the new one happens to be shorter, you'll want to truncate it before writing (you can only truncate immediately after opening the file - hence why it happens first). That appears to be outside of the scope of this question. I'm including the code to truncate, but not including the logic for figuring out whether or not you need to re-download the sample/if it is old.
function writeToFile(filename,data,callback){
fs.root.getFile(filename, {create: true}, function(fileEntry) {
fileEntry.createWriter(function(writer) {
writer.onwriteend = function(e) {
//we've truncated the file, now write the data
writer.onwriteend = function(e){
callback();
}
var blob = new Blob([data]);
writer.write(blob);
};
writer.truncate(0);
}, errorHandler);
}, errorHandler);
}
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);
});
So I ran into an issue where I was writing contents to a file via the HTML5 File-system api. The issue occurs when new content is shorter than the previous content, the old content is written-over as expected, but the tail of the old contents remain at the end of the file. The data I am writing is meta data for a given web-app and tends to change periodically, but not very often, generally increasing in size but occasionally the meta data is smaller in size.
Example, original content of file 0000000000, new content 11123 and after writing to the file, the contents become 1112300000
To get around this, I have been removing the file and passing a callback to write the new information in on every call. (cnDAO.filesystem is the filesystem object obtained when requesting persistent memory and has been initialized appropriately)
function writeToFile(fPath,data,callback){
rmFile(fPath,function(){
cnDAO.fileSystem.root.getFile(fPath, {
create: true
}, function(fileEntry) {
fileEntry.createWriter(function(writer) {
writer.onwriteend = function(e) {
callback();
};
writer.onerror = function(e3) { };
var blob = new Blob([data]);
writer.write(blob);
}, errorHandler);
}, errorHandler);
});
}
function rmFile(fPath,callback){
cnDAO.fileSystem.root.getFile(fPath, {
create: true
}, function(fileEntry) {
fileEntry.remove(callback);
}, errorHandler);
}
So, I was wondering if there was a better way to do what I am doing. truncate appeared in the following while I was searching for a solution (this post). As pointed out in the previous post truncate can only be called immediately after opening a file - Is truncate a better approach? Is what I'm doing better practice? Is there a quicker and easier way that I do not know about?
I would like to just start-fresh on every write to file- if that is plausible and/or good practice.
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.