/*
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
*/
|
|
|
|
var exec = require('cordova/exec'),
|
|
FileError = require('./FileError'),
|
|
ProgressEvent = require('./ProgressEvent');
|
|
|
|
/**
|
|
* This class writes to the mobile device file system.
|
|
*
|
|
* For Android:
|
|
* The root directory is the root of the file system.
|
|
* To write to the SD card, the file name is "sdcard/my_file.txt"
|
|
*
|
|
* @constructor
|
|
* @param file {File} File object containing file properties
|
|
* @param append if true write to the end of the file, otherwise overwrite the file
|
|
*/
|
|
var FileWriter = function(file) {
|
|
this.fileName = "";
|
|
this.length = 0;
|
|
if (file) {
|
|
this.localURL = file.localURL || file;
|
|
this.length = file.size || 0;
|
|
}
|
|
// default is to write at the beginning of the file
|
|
this.position = 0;
|
|
|
|
this.readyState = 0; // EMPTY
|
|
|
|
this.result = null;
|
|
|
|
// Error
|
|
this.error = null;
|
|
|
|
// Event handlers
|
|
this.onwritestart = null; // When writing starts
|
|
this.onprogress = null; // While writing the file, and reporting partial file data
|
|
this.onwrite = null; // When the write has successfully completed.
|
|
this.onwriteend = null; // When the request has completed (either in success or failure).
|
|
this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method.
|
|
this.onerror = null; // When the write has failed (see errors).
|
|
};
|
|
|
|
// States
|
|
FileWriter.INIT = 0;
|
|
FileWriter.WRITING = 1;
|
|
FileWriter.DONE = 2;
|
|
|
|
/**
|
|
* Abort writing file.
|
|
*/
|
|
FileWriter.prototype.abort = function() {
|
|
// check for invalid state
|
|
if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
|
|
throw new FileError(FileError.INVALID_STATE_ERR);
|
|
}
|
|
|
|
// set error
|
|
this.error = new FileError(FileError.ABORT_ERR);
|
|
|
|
this.readyState = FileWriter.DONE;
|
|
|
|
// If abort callback
|
|
if (typeof this.onabort === "function") {
|
|
this.onabort(new ProgressEvent("abort", {"target":this}));
|
|
}
|
|
|
|
// If write end callback
|
|
if (typeof this.onwriteend === "function") {
|
|
this.onwriteend(new ProgressEvent("writeend", {"target":this}));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Writes data to the file
|
|
*
|
|
* @param data text or blob to be written
|
|
* @param isPendingBlobReadResult {Boolean} true if the data is the pending blob read operation result
|
|
*/
|
|
FileWriter.prototype.write = function(data, isPendingBlobReadResult) {
|
|
|
|
var that=this;
|
|
var supportsBinary = (typeof window.Blob !== 'undefined' && typeof window.ArrayBuffer !== 'undefined');
|
|
var isProxySupportBlobNatively = (cordova.platformId === "windows8" || cordova.platformId === "windows");
|
|
var isBinary;
|
|
|
|
// Check to see if the incoming data is a blob
|
|
if (data instanceof File || (!isProxySupportBlobNatively && supportsBinary && data instanceof Blob)) {
|
|
var fileReader = new FileReader();
|
|
fileReader.onload = function() {
|
|
// Call this method again, with the arraybuffer as argument
|
|
FileWriter.prototype.write.call(that, this.result, true /* isPendingBlobReadResult */);
|
|
};
|
|
fileReader.onerror = function () {
|
|
// DONE state
|
|
that.readyState = FileWriter.DONE;
|
|
|
|
// Save error
|
|
that.error = this.error;
|
|
|
|
// If onerror callback
|
|
if (typeof that.onerror === "function") {
|
|
that.onerror(new ProgressEvent("error", {"target":that}));
|
|
}
|
|
|
|
// If onwriteend callback
|
|
if (typeof that.onwriteend === "function") {
|
|
that.onwriteend(new ProgressEvent("writeend", {"target":that}));
|
|
}
|
|
};
|
|
|
|
// WRITING state
|
|
this.readyState = FileWriter.WRITING;
|
|
|
|
if (supportsBinary) {
|
|
fileReader.readAsArrayBuffer(data);
|
|
} else {
|
|
fileReader.readAsText(data);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Mark data type for safer transport over the binary bridge
|
|
isBinary = supportsBinary && (data instanceof ArrayBuffer);
|
|
if (isBinary && cordova.platformId === "windowsphone") {
|
|
// create a plain array, using the keys from the Uint8Array view so that we can serialize it
|
|
data = Array.apply(null, new Uint8Array(data));
|
|
}
|
|
|
|
// Throw an exception if we are already writing a file
|
|
if (this.readyState === FileWriter.WRITING && !isPendingBlobReadResult) {
|
|
throw new FileError(FileError.INVALID_STATE_ERR);
|
|
}
|
|
|
|
// WRITING state
|
|
this.readyState = FileWriter.WRITING;
|
|
|
|
var me = this;
|
|
|
|
// If onwritestart callback
|
|
if (typeof me.onwritestart === "function") {
|
|
me.onwritestart(new ProgressEvent("writestart", {"target":me}));
|
|
}
|
|
|
|
// Write file
|
|
exec(
|
|
// Success callback
|
|
function(r) {
|
|
// If DONE (cancelled), then don't do anything
|
|
if (me.readyState === FileWriter.DONE) {
|
|
return;
|
|
}
|
|
|
|
// position always increases by bytes written because file would be extended
|
|
me.position += r;
|
|
// The length of the file is now where we are done writing.
|
|
|
|
me.length = me.position;
|
|
|
|
// DONE state
|
|
me.readyState = FileWriter.DONE;
|
|
|
|
// If onwrite callback
|
|
if (typeof me.onwrite === "function") {
|
|
me.onwrite(new ProgressEvent("write", {"target":me}));
|
|
}
|
|
|
|
// If onwriteend callback
|
|
if (typeof me.onwriteend === "function") {
|
|
me.onwriteend(new ProgressEvent("writeend", {"target":me}));
|
|
}
|
|
},
|
|
// Error callback
|
|
function(e) {
|
|
// If DONE (cancelled), then don't do anything
|
|
if (me.readyState === FileWriter.DONE) {
|
|
return;
|
|
}
|
|
|
|
// DONE state
|
|
me.readyState = FileWriter.DONE;
|
|
|
|
// Save error
|
|
me.error = new FileError(e);
|
|
|
|
// If onerror callback
|
|
if (typeof me.onerror === "function") {
|
|
me.onerror(new ProgressEvent("error", {"target":me}));
|
|
}
|
|
|
|
// If onwriteend callback
|
|
if (typeof me.onwriteend === "function") {
|
|
me.onwriteend(new ProgressEvent("writeend", {"target":me}));
|
|
}
|
|
}, "File", "write", [this.localURL, data, this.position, isBinary]);
|
|
};
|
|
|
|
/**
|
|
* Moves the file pointer to the location specified.
|
|
*
|
|
* If the offset is a negative number the position of the file
|
|
* pointer is rewound. If the offset is greater than the file
|
|
* size the position is set to the end of the file.
|
|
*
|
|
* @param offset is the location to move the file pointer to.
|
|
*/
|
|
FileWriter.prototype.seek = function(offset) {
|
|
// Throw an exception if we are already writing a file
|
|
if (this.readyState === FileWriter.WRITING) {
|
|
throw new FileError(FileError.INVALID_STATE_ERR);
|
|
}
|
|
|
|
if (!offset && offset !== 0) {
|
|
return;
|
|
}
|
|
|
|
// See back from end of file.
|
|
if (offset < 0) {
|
|
this.position = Math.max(offset + this.length, 0);
|
|
}
|
|
// Offset is bigger than file size so set position
|
|
// to the end of the file.
|
|
else if (offset > this.length) {
|
|
this.position = this.length;
|
|
}
|
|
// Offset is between 0 and file size so set the position
|
|
// to start writing.
|
|
else {
|
|
this.position = offset;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Truncates the file to the size specified.
|
|
*
|
|
* @param size to chop the file at.
|
|
*/
|
|
FileWriter.prototype.truncate = function(size) {
|
|
// Throw an exception if we are already writing a file
|
|
if (this.readyState === FileWriter.WRITING) {
|
|
throw new FileError(FileError.INVALID_STATE_ERR);
|
|
}
|
|
|
|
// WRITING state
|
|
this.readyState = FileWriter.WRITING;
|
|
|
|
var me = this;
|
|
|
|
// If onwritestart callback
|
|
if (typeof me.onwritestart === "function") {
|
|
me.onwritestart(new ProgressEvent("writestart", {"target":this}));
|
|
}
|
|
|
|
// Write file
|
|
exec(
|
|
// Success callback
|
|
function(r) {
|
|
// If DONE (cancelled), then don't do anything
|
|
if (me.readyState === FileWriter.DONE) {
|
|
return;
|
|
}
|
|
|
|
// DONE state
|
|
me.readyState = FileWriter.DONE;
|
|
|
|
// Update the length of the file
|
|
me.length = r;
|
|
me.position = Math.min(me.position, r);
|
|
|
|
// If onwrite callback
|
|
if (typeof me.onwrite === "function") {
|
|
me.onwrite(new ProgressEvent("write", {"target":me}));
|
|
}
|
|
|
|
// If onwriteend callback
|
|
if (typeof me.onwriteend === "function") {
|
|
me.onwriteend(new ProgressEvent("writeend", {"target":me}));
|
|
}
|
|
},
|
|
// Error callback
|
|
function(e) {
|
|
// If DONE (cancelled), then don't do anything
|
|
if (me.readyState === FileWriter.DONE) {
|
|
return;
|
|
}
|
|
|
|
// DONE state
|
|
me.readyState = FileWriter.DONE;
|
|
|
|
// Save error
|
|
me.error = new FileError(e);
|
|
|
|
// If onerror callback
|
|
if (typeof me.onerror === "function") {
|
|
me.onerror(new ProgressEvent("error", {"target":me}));
|
|
}
|
|
|
|
// If onwriteend callback
|
|
if (typeof me.onwriteend === "function") {
|
|
me.onwriteend(new ProgressEvent("writeend", {"target":me}));
|
|
}
|
|
}, "File", "truncate", [this.localURL, size]);
|
|
};
|
|
|
|
module.exports = FileWriter;
|