File API: System and Directories

http://www.w3.org/TR/file-system-api/

User can retrieve PERSISTENT and TEMPORARY file systems, list their
contents, and manipulate files and directories within them.

Modify existing FileWriter implementation
-----------------------------------------

  - Change the way user creates a FileWriter.  User must either pass a
    File object to the FileWriter constructor, or use the
    FileEntry.createWriter() method.

  - Drop support for the 'filePath' and 'append' parameters in the
    FileWriter constructor.  The file path is determined from either the
    File object passed to the FileWriter constructor, or the FileEntry
    object used to create the FileWriter.  To append to a file, use the
    FileWriter object's seek method:

    // writer is a FileWriter object
    // seek to length of file to append
    writer.seek(writer.length);

Replace FileMgr JavaScript APIs not specified in any File API spec
------------------------------------------------------------------

  - Remove navigator.fileMgr.createDirectory(dirName) function.  To
    create a directory, use the DirectoryEntry.getDirectory() method,
    which is part of the File API: Directories and System spec.  Set
    the Flags.create to 'true':

    // directory is a DirectoryEntry object
    directory.getDirectory(path, {create:true}, successCB, failCB);

  - Remove navigator.fileMgr.getRootPaths() function.  To retrieve the
    root file systems, use the window.requestFileSystem() function,
    which is part of the File API: Directories and System spec.

  - Remove navigator.fileMgr.getFileProperties(fileName) function.  To
    get the properties of a file, use the FileEntry.file() method, which
    is part of the File API: Directories and System spec.

  - Remove navigator.fileMgr.deleteFile(fileName) function.  To delete a
    file, use the Entry.remove() method, which is part of the File API:
    Directories and System spec.

  - Remove navigator.fileMgr.deleteDirectory(dirName) function.  To
    delete a directory, use the Entry.remove() (if it is empty), or
    DirectoryEntry.removeRecursively() methods, which are part of the
    File API: Directories and System spec.

Clean up existing FileManager native code.  Move some functionality to
file utility class.
This commit is contained in:
macdonst 2011-02-04 06:44:33 +08:00
parent 64d4337d5f
commit 0c3a8fb9f7
10 changed files with 1356 additions and 318 deletions

View File

@ -22,6 +22,23 @@ function FileProperties(filePath) {
this.lastModifiedDate = null;
}
/**
* Represents a single file.
*
* name {DOMString} name of the file, without path information
* fullPath {DOMString} the full path of the file, including the name
* type {DOMString} mime type
* lastModifiedDate {Date} last modified date
* size {Number} size of the file in bytes
*/
function File(name, fullPath, type, lastModifiedDate, size) {
this.name = name || null;
this.fullPath = fullPath || null;
this.type = type || null;
this.lastModifiedDate = lastModifiedDate || null;
this.size = size || 0;
}
/**
* Create an event object since we can't set target on DOM event.
*
@ -54,6 +71,10 @@ FileError.ENCODING_ERR = 5;
FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
FileError.INVALID_STATE_ERR = 7;
FileError.SYNTAX_ERR = 8;
FileError.INVALID_MODIFICATION_ERR = 9;
FileError.QUOTA_EXCEEDED_ERR = 10;
FileError.TYPE_MISMATCH_ERR = 11;
FileError.PATH_EXISTS_ERR = 12;
//-----------------------------------------------------------------------------
// File manager
@ -63,16 +84,12 @@ function FileMgr() {
}
FileMgr.prototype.getFileProperties = function(filePath) {
return PhoneGap.exec(null, null, "File", "getFile", [filePath]);
return PhoneGap.exec(null, null, "File", "getFileProperties", [filePath]);
};
FileMgr.prototype.getFileBasePaths = function() {
};
FileMgr.prototype.getRootPaths = function() {
return PhoneGap.exec(null, null, "File", "getRootPaths", []);
};
FileMgr.prototype.testSaveLocationExists = function(successCallback, errorCallback) {
return PhoneGap.exec(successCallback, errorCallback, "File", "testSaveLocationExists", []);
};
@ -85,18 +102,6 @@ FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, error
return PhoneGap.exec(successCallback, errorCallback, "File", "testDirectoryExists", [dirName]);
};
FileMgr.prototype.createDirectory = function(dirName, successCallback, errorCallback) {
return PhoneGap.exec(successCallback, errorCallback, "File", "createDirectory", [dirName]);
};
FileMgr.prototype.deleteDirectory = function(dirName, successCallback, errorCallback) {
return PhoneGap.exec(successCallback, errorCallback, "File", "deleteDirectory", [dirName]);
};
FileMgr.prototype.deleteFile = function(fileName, successCallback, errorCallback) {
return PhoneGap.exec(successCallback, errorCallback, "File", "deleteFile", [fileName]);
};
FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback) {
return PhoneGap.exec(successCallback, errorCallback, "File", "getFreeDiskSpace", []);
};
@ -198,11 +203,16 @@ FileReader.prototype.abort = function() {
/**
* Read text file.
*
* @param file The name of the file
* @param file {File} File object containing file properties
* @param encoding [Optional] (see http://www.iana.org/assignments/character-sets)
*/
FileReader.prototype.readAsText = function(file, encoding) {
this.fileName = file;
this.fileName = "";
if (typeof file.fullPath === "undefined") {
this.fileName = file;
} else {
this.fileName = file.fullPath;
}
// LOADING state
this.readyState = FileReader.LOADING;
@ -219,7 +229,7 @@ FileReader.prototype.readAsText = function(file, encoding) {
var me = this;
// Read file
navigator.fileMgr.readAsText(file, enc,
navigator.fileMgr.readAsText(this.fileName, enc,
// Success callback
function(r) {
@ -284,10 +294,15 @@ FileReader.prototype.readAsText = function(file, encoding) {
* A data url is of the form:
* data:[<mediatype>][;base64],<data>
*
* @param file The name of the file
* @param file {File} File object containing file properties
*/
FileReader.prototype.readAsDataURL = function(file) {
this.fileName = file;
this.fileName = "";
if (typeof file.fullPath === "undefined") {
this.fileName = file;
} else {
this.fileName = file.fullPath;
}
// LOADING state
this.readyState = FileReader.LOADING;
@ -301,7 +316,7 @@ FileReader.prototype.readAsDataURL = function(file) {
var me = this;
// Read file
navigator.fileMgr.readAsDataURL(file,
navigator.fileMgr.readAsDataURL(this.fileName,
// Success callback
function(r) {
@ -363,7 +378,7 @@ FileReader.prototype.readAsDataURL = function(file) {
/**
* Read file and return data as a binary data.
*
* @param file The name of the file
* @param file {File} File object containing file properties
*/
FileReader.prototype.readAsBinaryString = function(file) {
// TODO - Can't return binary data to browser.
@ -373,7 +388,7 @@ FileReader.prototype.readAsBinaryString = function(file) {
/**
* Read file and return data as a binary data.
*
* @param file The name of the file
* @param file {File} File object containing file properties
*/
FileReader.prototype.readAsArrayBuffer = function(file) {
// TODO - Can't return binary data to browser.
@ -391,19 +406,18 @@ FileReader.prototype.readAsArrayBuffer = function(file) {
* The root directory is the root of the file system.
* To write to the SD card, the file name is "sdcard/my_file.txt"
*
* @param filePath the file to write to
* @param file {File} File object containing file properties
* @param append if true write to the end of the file, otherwise overwrite the file
*/
function FileWriter(filePath, append) {
function FileWriter(file) {
this.fileName = "";
this.length = 0;
if (filePath) {
var f = navigator.fileMgr.getFileProperties(filePath);
this.fileName = f.name;
this.length = f.size;
if (file) {
this.fileName = file.fullPath || file;
this.length = file.size || 0;
}
// default is to write at the beginning of the file
this.position = (append !== true) ? 0 : this.length;
this.position = 0;
this.readyState = 0; // EMPTY
@ -753,3 +767,418 @@ FileWriter.prototype.truncate = function(size) {
);
};
function LocalFileSystem() {
};
// File error codes
LocalFileSystem.TEMPORARY = 0;
LocalFileSystem.PERSISTENT = 1;
LocalFileSystem.RESOURCE = 2;
LocalFileSystem.APPLICATION = 3;
/**
* Requests a filesystem in which to store application data.
*
* @param {int} type of file system being requested
* @param {Function} successCallback is called with the new FileSystem
* @param {Function} errorCallback is called with a FileError
*/
LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) {
if (type < 0 || type > 3) {
if (typeof errorCallback == "function") {
errorCallback({
"code": FileError.SYNTAX_ERR
});
}
}
else {
PhoneGap.exec(successCallback, errorCallback, "File", "requestFileSystem", [type, size]);
}
};
/**
*
* @param {DOMString} uri referring to a local file in a filesystem
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "resolveLocalFileSystemURI", [uri]);
};
/**
* This function returns and array of contacts. It is required as we need to convert raw
* JSON objects into concrete Contact objects. Currently this method is called after
* navigator.service.contacts.find but before the find methods success call back.
*
* @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects.
* @returns an entry
*/
LocalFileSystem.prototype._castFS = function(pluginResult) {
var entry = null;
entry = new DirectoryEntry();
entry.isDirectory = pluginResult.message.root.isDirectory;
entry.isFile = pluginResult.message.root.isFile;
entry.name = pluginResult.message.root.name;
entry.fullPath = pluginResult.message.root.fullPath;
pluginResult.message.root = entry;
return pluginResult;
}
LocalFileSystem.prototype._castEntry = function(pluginResult) {
var entry = null;
if (pluginResult.message.isDirectory) {
console.log("This is a dir");
entry = new DirectoryEntry();
}
else if (pluginResult.message.isFile) {
console.log("This is a file");
entry = new FileEntry();
}
entry.isDirectory = pluginResult.message.isDirectory;
entry.isFile = pluginResult.message.isFile;
entry.name = pluginResult.message.name;
entry.fullPath = pluginResult.message.fullPath;
pluginResult.message = entry;
return pluginResult;
}
LocalFileSystem.prototype._castEntries = function(pluginResult) {
var entries = pluginResult.message;
var retVal = [];
for (i=0; i<entries.length; i++) {
retVal.push(window.localFileSystem._createEntry(entries[i]));
}
pluginResult.message = retVal;
return pluginResult;
}
LocalFileSystem.prototype._createEntry = function(castMe) {
var entry = null;
if (castMe.isDirectory) {
console.log("This is a dir");
entry = new DirectoryEntry();
}
else if (castMe.isFile) {
console.log("This is a file");
entry = new FileEntry();
}
entry.isDirectory = castMe.isDirectory;
entry.isFile = castMe.isFile;
entry.name = castMe.name;
entry.fullPath = castMe.fullPath;
return entry;
}
LocalFileSystem.prototype._castDate = function(pluginResult) {
if (pluginResult.message.modificationTime) {
var modTime = new Date(pluginResult.message.modificationTime);
pluginResult.message.modificationTime = modTime;
}
else if (pluginResult.message.lastModifiedDate) {
var file = new File();
file.size = pluginResult.message.size;
file.type = pluginResult.message.type;
file.name = pluginResult.message.name;
file.fullPath = pluginResult.message.fullPath;
file.lastModifedDate = new Date(pluginResult.message.lastModifiedDate);
pluginResult.message = file;
}
return pluginResult;
}
/**
* Information about the state of the file or directory
*
* {Date} modificationTime (readonly)
*/
function Metadata() {
this.modificationTime=null;
};
/**
* Supplies arguments to methods that lookup or create files and directories
*
* @param {boolean} create file or directory if it doesn't exist
* @param {boolean} exclusive if true the command will fail if the file or directory exists
*/
function Flags(create, exclusive) {
this.create = create || false;
this.exclusive = exclusive || false;
};
/**
* An interface representing a file system
*
* {DOMString} name the unique name of the file system (readonly)
* {DirectoryEntry} root directory of the file system (readonly)
*/
function FileSystem() {
this.name = null;
this.root = null;
};
/**
* An interface representing a directory on the file system.
*
* {boolean} isFile always false (readonly)
* {boolean} isDirectory always true (readonly)
* {DOMString} name of the directory, excluding the path leading to it (readonly)
* {DOMString} fullPath the absolute full path to the directory (readonly)
* {FileSystem} filesystem on which the directory resides (readonly)
*/
function DirectoryEntry() {
this.isFile = false;
this.isDirectory = true;
this.name = null;
this.fullPath = null;
this.filesystem = null;
};
/**
* Copies a directory to a new location
*
* @param {DirectoryEntry} parent the directory to which to copy the entry
* @param {DOMString} newName the new name of the entry, defaults to the current name
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]);
};
/**
* Looks up the metadata of the entry
*
* @param {Function} successCallback is called with a Metadata object
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.getMetadata = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]);
};
/**
* Gets the parent of the entry
*
* @param {Function} successCallback is called with a parent entry
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.getParent = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]);
};
/**
* Moves a directory to a new location
*
* @param {DirectoryEntry} parent the directory to which to move the entry
* @param {DOMString} newName the new name of the entry, defaults to the current name
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]);
};
/**
* Removes the entry
*
* @param {Function} successCallback is called with no parameters
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.remove = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]);
};
/**
* Returns a URI that can be used to identify this entry.
*
* @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
* @return uri
*/
DirectoryEntry.prototype.toURI = function(mimeType) {
return "file://" + this.fullPath;
};
/**
* Creates a new DirectoryReader to read entries from this directory
*/
DirectoryEntry.prototype.createReader = function(successCallback, errorCallback) {
return new DirectoryReader(this.fullPath);
};
/**
* Creates or looks up a directory
*
* @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
* @param {Flags} options to create or excluively create the directory
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "getDirectory", [this.fullPath, path, options]);
};
/**
* Creates or looks up a file
*
* @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
* @param {Flags} options to create or excluively create the file
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "getFile", [this.fullPath, path, options]);
};
/**
* Deletes a directory and all of it's contents
*
* @param {Function} successCallback is called with no parameters
* @param {Function} errorCallback is called with a FileError
*/
DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "removeRecursively", [this.fullPath]);
};
/**
* An interface that lists the files and directories in a directory.
*/
function DirectoryReader(fullPath){
this.fullPath = fullPath || null;
};
/**
* Returns a list of entries from a directory.
*
* @param {Function} successCallback is called with a list of entries
* @param {Function} errorCallback is called with a FileError
*/
DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "readEntries", [this.fullPath]);
}
/**
* An interface representing a directory on the file system.
*
* {boolean} isFile always true (readonly)
* {boolean} isDirectory always false (readonly)
* {DOMString} name of the file, excluding the path leading to it (readonly)
* {DOMString} fullPath the absolute full path to the file (readonly)
* {FileSystem} filesystem on which the directory resides (readonly)
*/
function FileEntry() {
this.isFile = true;
this.isDirectory = false;
this.name = null;
this.fullPath = null;
this.filesystem = null;
};
/**
* Copies a file to a new location
*
* @param {DirectoryEntry} parent the directory to which to copy the entry
* @param {DOMString} newName the new name of the entry, defaults to the current name
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
FileEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]);
};
/**
* Looks up the metadata of the entry
*
* @param {Function} successCallback is called with a Metadata object
* @param {Function} errorCallback is called with a FileError
*/
FileEntry.prototype.getMetadata = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]);
};
/**
* Gets the parent of the entry
*
* @param {Function} successCallback is called with a parent entry
* @param {Function} errorCallback is called with a FileError
*/
FileEntry.prototype.getParent = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]);
};
/**
* Moves a directory to a new location
*
* @param {DirectoryEntry} parent the directory to which to move the entry
* @param {DOMString} newName the new name of the entry, defaults to the current name
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
FileEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]);
};
/**
* Removes the entry
*
* @param {Function} successCallback is called with no parameters
* @param {Function} errorCallback is called with a FileError
*/
FileEntry.prototype.remove = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]);
};
/**
* Returns a URI that can be used to identify this entry.
*
* @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
* @return uri
*/
FileEntry.prototype.toURI = function(mimeType) {
return "file://" + this.fullPath;
};
/**
* Creates a new FileWriter associated with the file that this FileEntry represents.
*
* @param {Function} successCallback is called with the new FileWriter
* @param {Function} errorCallback is called with a FileError
*/
FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
var writer = new FileWriter(this.fullPath);
if (writer.fileName == null || writer.fileName == "") {
if (typeof errorCallback == "function") {
errorCallback({
"code": FileError.INVALID_STATE_ERR
});
}
}
if (typeof successCallback == "function") {
successCallback(writer);
}
};
/**
* Returns a File that represents the current state of the file that this FileEntry represents.
*
* @param {Function} successCallback is called with the new File object
* @param {Function} errorCallback is called with a FileError
*/
FileEntry.prototype.file = function(successCallback, errorCallback) {
PhoneGap.exec(successCallback, errorCallback, "File", "getFileMetadata", [this.fullPath]);
};
/**
* Add the FileSystem interface into the browser.
*/
PhoneGap.addConstructor(function() {
var pgLocalFileSystem = new LocalFileSystem();
// Needed for cast methods
if(typeof window.localFileSystem == "undefined") window.localFileSystem = pgLocalFileSystem;
if(typeof window.requestFileSystem == "undefined") window.requestFileSystem = pgLocalFileSystem.requestFileSystem;
if(typeof window.resolveLocalFileSystemURI == "undefined") window.resolveLocalFileSystemURI = pgLocalFileSystem.resolveLocalFileSystemURI;
});

View File

@ -394,42 +394,44 @@ PhoneGap.stringify = function(args) {
var s = "[";
var i, type, start, name, nameType, a;
for (i = 0; i < args.length; i++) {
if (i > 0) {
s = s + ",";
}
type = typeof args[i];
if ((type === "number") || (type === "boolean")) {
s = s + args[i];
} else if (args[i] instanceof Array) {
s = s + "[" + args[i] + "]";
} else if (args[i] instanceof Object) {
start = true;
s = s + '{';
for (name in args[i]) {
if (args[i][name] !== null) {
if (!start) {
s = s + ',';
}
s = s + '"' + name + '":';
nameType = typeof args[i][name];
if ((nameType === "number") || (nameType === "boolean")) {
s = s + args[i][name];
} else if ((typeof args[i][name]) === 'function') {
// don't copy the functions
s = s + '""';
} else if (args[i][name] instanceof Object) {
s = s + this.stringify(args[i][name]);
} else {
s = s + '"' + args[i][name] + '"';
}
start = false;
}
if (args[i] != null) {
if (i > 0) {
s = s + ",";
}
type = typeof args[i];
if ((type === "number") || (type === "boolean")) {
s = s + args[i];
} else if (args[i] instanceof Array) {
s = s + "[" + args[i] + "]";
} else if (args[i] instanceof Object) {
start = true;
s = s + '{';
for (name in args[i]) {
if (args[i][name] !== null) {
if (!start) {
s = s + ',';
}
s = s + '"' + name + '":';
nameType = typeof args[i][name];
if ((nameType === "number") || (nameType === "boolean")) {
s = s + args[i][name];
} else if ((typeof args[i][name]) === 'function') {
// don't copy the functions
s = s + '""';
} else if (args[i][name] instanceof Object) {
s = s + this.stringify(args[i][name]);
} else {
s = s + '"' + args[i][name] + '"';
}
start = false;
}
}
s = s + '}';
} else {
a = args[i].replace(/\\/g, '\\\\');
a = a.replace(/"/g, '\\"');
s = s + '"' + a + '"';
}
s = s + '}';
} else {
a = args[i].replace(/\\/g, '\\\\');
a = a.replace(/"/g, '\\"');
s = s + '"' + a + '"';
}
}
s = s + "]";

View File

@ -8,15 +8,9 @@
package com.phonegap;
import java.io.File;
import java.util.Date;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;
/**
* This class provides file directory utilities.
@ -78,29 +72,6 @@ public class DirectoryManager {
return (freeSpace);
}
/**
* Create directory on SD card.
*
* @param directoryName The name of the directory to create.
* @return T=successful, F=failed
*/
protected static boolean createDirectory(String directoryName) {
boolean status;
// Make sure SD card exists
if ((testSaveLocationExists()) && (!directoryName.equals(""))) {
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), directoryName);
status = newPath.mkdir();
status = true;
}
// If no SD card or invalid dir name
else {
status = false;
}
return status;
}
/**
* Determine if SD card exists.
@ -123,95 +94,6 @@ public class DirectoryManager {
return status;
}
/**
* Delete directory.
*
* @param fileName The name of the directory to delete
* @return T=deleted, F=could not delete
*/
protected static boolean deleteDirectory(String fileName) {
boolean status;
SecurityManager checker = new SecurityManager();
// Make sure SD card exists
if ((testSaveLocationExists()) && (!fileName.equals(""))) {
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), fileName);
checker.checkDelete(newPath.toString());
// If dir to delete is really a directory
if (newPath.isDirectory()) {
String[] listfile = newPath.list();
// Delete all files within the specified directory and then delete the directory
try{
for (int i=0; i < listfile.length; i++){
File deletedFile = new File (newPath.toString()+"/"+listfile[i].toString());
deletedFile.delete();
}
newPath.delete();
Log.i("DirectoryManager deleteDirectory", fileName);
status = true;
}
catch (Exception e){
e.printStackTrace();
status = false;
}
}
// If dir not a directory, then error
else {
status = false;
}
}
// If no SD card
else {
status = false;
}
return status;
}
/**
* Delete file.
*
* @param fileName The name of the file to delete
* @return T=deleted, F=not deleted
*/
protected static boolean deleteFile(String fileName) {
boolean status;
SecurityManager checker = new SecurityManager();
// Make sure SD card exists
if ((testSaveLocationExists()) && (!fileName.equals(""))) {
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), fileName);
checker.checkDelete(newPath.toString());
// If file to delete is really a file
if (newPath.isFile()){
try {
Log.i("DirectoryManager deleteFile", fileName);
newPath.delete();
status = true;
}catch (SecurityException se){
se.printStackTrace();
status = false;
}
}
// If not a file, then error
else {
status = false;
}
}
// If no SD card
else {
status = false;
}
return status;
}
/**
* Create a new file object from two file paths.
*
@ -229,45 +111,4 @@ public class DirectoryManager {
}
return newPath;
}
/**
* This method will determine the file properties of the file specified
* by the filePath. Creates a JSONObject with name, lastModifiedDate and
* size properties.
*
* @param filePath the file to get the properties of
* @return a JSONObject with the files properties
*/
protected static JSONObject getFile(String filePath) {
File fp = new File(filePath);
JSONObject obj = new JSONObject();
try {
obj.put("name", fp.getAbsolutePath());
obj.put("lastModifiedDate", new Date(fp.lastModified()).toString());
obj.put("size", fp.length());
}
catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return obj;
}
/**
* This method returns a JSONArray of file paths. Android's default
* location where files can be written is Environment.getExternalStorageDirectory().
* We are returning a array with one element so the interface can remain
* consistent with BlackBerry as they have two areas where files can be
* written.
*
* @return an array of file paths
*/
protected static JSONArray getRootPaths() {
JSONArray retVal = new JSONArray();
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
retVal.put(path);
return retVal;
}
}

View File

@ -8,6 +8,8 @@
package com.phonegap;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.FileChannel;
import org.apache.commons.codec.binary.Base64;
@ -16,16 +18,25 @@ import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import android.webkit.MimeTypeMap;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import com.phonegap.file.EncodingException;
import com.phonegap.file.FileExistsException;
import com.phonegap.file.InvalidModificationException;
import com.phonegap.file.NoModificationAllowedException;
import com.phonegap.file.TypeMismatchException;
/**
* This class provides SD card file and directory services to JavaScript.
* Only files on the SD card can be accessed.
*/
public class FileUtils extends Plugin {
private static final String LOG_TAG = "FileUtils";
public static int NOT_FOUND_ERR = 1;
public static int SECURITY_ERR = 2;
public static int ABORT_ERR = 3;
@ -35,7 +46,16 @@ public class FileUtils extends Plugin {
public static int NO_MODIFICATION_ALLOWED_ERR = 6;
public static int INVALID_STATE_ERR = 7;
public static int SYNTAX_ERR = 8;
public static int INVALID_MODIFICATION_ERR = 9;
public static int QUOTA_EXCEEDED_ERR = 10;
public static int TYPE_MISMATCH_ERR = 11;
public static int PATH_EXISTS_ERR = 12;
public static int TEMPORARY = 0;
public static int PERSISTENT = 1;
public static int RESOURCE = 2;
public static int APPLICATION = 3;
FileReader f_in;
FileWriter f_out;
@ -59,114 +79,763 @@ public class FileUtils extends Plugin {
//System.out.println("FileUtils.execute("+action+")");
try {
if (action.equals("testSaveLocationExists")) {
boolean b = DirectoryManager.testSaveLocationExists();
return new PluginResult(status, b);
}
else if (action.equals("getFreeDiskSpace")) {
long l = DirectoryManager.getFreeDiskSpace();
return new PluginResult(status, l);
}
else if (action.equals("testFileExists")) {
boolean b = DirectoryManager.testFileExists(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("testDirectoryExists")) {
boolean b = DirectoryManager.testFileExists(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("deleteDirectory")) {
boolean b = DirectoryManager.deleteDirectory(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("deleteFile")) {
boolean b = DirectoryManager.deleteFile(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("createDirectory")) {
boolean b = DirectoryManager.createDirectory(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("getRootPaths")) {
return new PluginResult(status, DirectoryManager.getRootPaths());
}
else if (action.equals("readAsText")) {
try {
String s = this.readAsText(args.getString(0), args.getString(1));
return new PluginResult(status, s);
} catch (FileNotFoundException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
try {
if (action.equals("testSaveLocationExists")) {
boolean b = DirectoryManager.testSaveLocationExists();
return new PluginResult(status, b);
}
else if (action.equals("getFreeDiskSpace")) {
long l = DirectoryManager.getFreeDiskSpace();
return new PluginResult(status, l);
}
else if (action.equals("testFileExists")) {
boolean b = DirectoryManager.testFileExists(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("testDirectoryExists")) {
boolean b = DirectoryManager.testFileExists(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("readAsText")) {
try {
String s = this.readAsText(args.getString(0), args.getString(1));
return new PluginResult(status, s);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
}
}
else if (action.equals("readAsDataURL")) {
try {
String s = this.readAsDataURL(args.getString(0));
return new PluginResult(status, s);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
}
}
else if (action.equals("writeAsText")) {
try {
this.writeAsText(args.getString(0), args.getString(1), args.getBoolean(2));
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
}
}
else if (action.equals("write")) {
try {
long fileSize = this.write(args.getString(0), args.getString(1), args.getLong(2));
return new PluginResult(status, fileSize);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
}
}
else if (action.equals("truncate")) {
try {
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
return new PluginResult(status, fileSize);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
}
}
else if (action.equals("requestFileSystem")) {
long size = args.optLong(1);
if (size != 0) {
if (size > DirectoryManager.getFreeDiskSpace()) {
JSONObject error = new JSONObject().put("code", FileUtils.QUOTA_EXCEEDED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
}
JSONObject obj = requestFileSystem(args.getInt(0));
return new PluginResult(status, obj, "window.localFileSystem._castFS");
}
}
else if (action.equals("readAsDataURL")) {
try {
String s = this.readAsDataURL(args.getString(0));
return new PluginResult(status, s);
} catch (FileNotFoundException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
else if (action.equals("resolveLocalFileSystemURI")) {
JSONObject obj = resolveLocalFileSystemURI(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
}
else if (action.equals("writeAsText")) {
try {
this.writeAsText(args.getString(0), args.getString(1), args.getBoolean(2));
} catch (FileNotFoundException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
else if (action.equals("getMetadata")) {
JSONObject obj = getMetadata(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castDate");
}
}
else if (action.equals("write")) {
try {
long fileSize = this.write(args.getString(0), args.getString(1), args.getLong(2));
return new PluginResult(status, fileSize);
} catch (FileNotFoundException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
else if (action.equals("getFileMetadata")) {
JSONObject obj = getFileMetadata(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castDate");
}
}
else if (action.equals("truncate")) {
try {
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
return new PluginResult(status, fileSize);
} catch (FileNotFoundException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
} catch (IOException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
else if (action.equals("getParent")) {
JSONObject obj = getParent(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
}
else if (action.equals("getFile")) {
JSONObject obj = DirectoryManager.getFile(args.getString(0));
return new PluginResult(status, obj);
else if (action.equals("getDirectory")) {
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), true);
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
else if (action.equals("getFile")) {
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), false);
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
else if (action.equals("remove")) {
boolean success;
success = remove(args.getString(0));
if (success) {
return new PluginResult(status);
} else {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
}
else if (action.equals("removeRecursively")) {
boolean success = removeRecursively(args.getString(0));
if (success) {
return new PluginResult(status);
} else {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
}
else if (action.equals("moveTo")) {
JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), true);
return new PluginResult(status, entry, "window.localFileSystem._castEntry");
}
else if (action.equals("copyTo")) {
JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), false);
return new PluginResult(status, entry, "window.localFileSystem._castEntry");
}
else if (action.equals("readEntries")) {
JSONArray entries = readEntries(args.getString(0));
return new PluginResult(status, entries, "window.localFileSystem._castEntries");
}
return new PluginResult(status, result);
} catch (FileNotFoundException e) {
JSONObject error = new JSONObject().put("code", FileUtils.NOT_FOUND_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (FileExistsException e) {
JSONObject error = new JSONObject().put("code", FileUtils.PATH_EXISTS_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (NoModificationAllowedException e) {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (JSONException e) {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (InvalidModificationException e) {
JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (MalformedURLException e) {
JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (IOException e) {
JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (EncodingException e) {
JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (TypeMismatchException e) {
JSONObject error = new JSONObject().put("code", FileUtils.TYPE_MISMATCH_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
return new PluginResult(status, result);
} catch (JSONException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
/**
* Allows the user to look up the Entry for a file or directory referred to by a local URI.
*
* @param url of the file/directory to look up
* @return a JSONObject representing a Entry from the filesystem
* @throws MalformedURLException if the url is not valid
* @throws FileNotFoundException if the file does not exist
* @throws IOException if the user can't read the file
* @throws JSONException
*/
private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException {
// Test to see if this is a valid URL first
@SuppressWarnings("unused")
URL testUrl = new URL(url);
File fp = null;
if (url.startsWith("file://")) {
fp = new File(url.substring(7, url.length()));
} else {
fp = new File(url);
}
if (!fp.exists()) {
throw new FileNotFoundException();
}
if (!fp.canRead()) {
throw new IOException();
}
return getEntry(fp);
}
/**
* Read the list of files from this directory.
*
* @param fileName the directory to read from
* @return a JSONArray containing JSONObjects that represent Entry objects.
* @throws FileNotFoundException if the directory is not found.
* @throws JSONException
*/
private JSONArray readEntries(String fileName) throws FileNotFoundException, JSONException {
File fp = new File(fileName);
if (!fp.exists()) {
// The directory we are listing doesn't exist so we should fail.
throw new FileNotFoundException();
}
JSONArray entries = new JSONArray();
if (fp.isDirectory()) {
File[] files = fp.listFiles();
for (int i=0; i<files.length; i++) {
entries.put(getEntry(files[i]));
}
}
return entries;
}
/**
* A setup method that handles the move/copy of files/directories
*
* @param fileName to be copied/moved
* @param newParent is the location where the file will be copied/moved to
* @param newName for the file directory to be called, if null use existing file name
* @param move if false do a copy, if true do a move
* @return a Entry object
* @throws FileExistsException
* @throws NoModificationAllowedException
* @throws IOException
* @throws InvalidModificationException
* @throws EncodingException
* @throws JSONException
*/
private JSONObject transferTo(String fileName, JSONObject newParent, String newName, boolean move) throws JSONException, FileExistsException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException {
// Check for invalid file name
if (newName != null && newName.contains(":")) {
throw new EncodingException("Bad file name");
}
File source = new File(fileName);
if (!source.exists()) {
// The file/directory we are copying doesn't exist so we should fail.
throw new FileNotFoundException("The source does not exist");
}
File destinationDir = new File(newParent.getString("fullPath"));
if (!destinationDir.exists()) {
// The destination does not exist so we should fail.
throw new FileNotFoundException("The source does not exist");
}
// Figure out where we should be copying to
File destination = createDestination(newName, source, destinationDir);
//Log.d(LOG_TAG, "Source: " + source.getAbsolutePath());
//Log.d(LOG_TAG, "Destin: " + destination.getAbsolutePath());
// Check to see if source and destination are the same file
if (source.getAbsolutePath().equals(destination.getAbsolutePath())) {
throw new InvalidModificationException("Can't copy a file onto itself");
}
if (source.isDirectory()) {
if (move) {
return moveDirectory(source, destination);
} else {
return copyDirectory(source, destination);
}
} else {
if (move) {
return moveFile(source, destination);
} else {
return copyFile(source, destination);
}
}
}
/**
* Creates the destination File object based on name passed in
*
* @param newName for the file directory to be called, if null use existing file name
* @param fp represents the source file
* @param destination represents the destination file
* @return a File object that represents the destination
*/
private File createDestination(String newName, File fp, File destination) {
File destFile = null;
// I know this looks weird but it is to work around a JSON bug.
if ("null".equals(newName) || "".equals(newName) ) {
newName = null;
}
if (newName != null) {
destFile = new File(destination.getAbsolutePath() + File.separator + newName);
} else {
destFile = new File(destination.getAbsolutePath() + File.separator + fp.getName());
}
return destFile;
}
/**
* Copy a file
*
* @param srcFile file to be copied
* @param destFile destination to be copied to
* @return a FileEntry object
* @throws IOException
* @throws InvalidModificationException
* @throws JSONException
*/
private JSONObject copyFile(File srcFile, File destFile) throws IOException, InvalidModificationException, JSONException {
// Renaming a file to an existing directory should fail
if (destFile.exists() && destFile.isDirectory()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
FileChannel input = new FileInputStream(srcFile).getChannel();
FileChannel output = new FileOutputStream(destFile).getChannel();
input.transferTo(0, input.size(), output);
input.close();
output.close();
/*
if (srcFile.length() != destFile.length()) {
return false;
}
*/
return getEntry(destFile);
}
/**
* Copy a directory
*
* @param srcDir directory to be copied
* @param destinationDir destination to be copied to
* @return a DirectoryEntry object
* @throws JSONException
* @throws IOException
* @throws NoModificationAllowedException
* @throws InvalidModificationException
*/
private JSONObject copyDirectory(File srcDir, File destinationDir) throws JSONException, IOException, NoModificationAllowedException, InvalidModificationException {
// Renaming a file to an existing directory should fail
if (destinationDir.exists() && destinationDir.isFile()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
// Check to make sure we are not copying the directory into itself
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
throw new InvalidModificationException("Can't copy itself into itself");
}
// See if the destination directory exists. If not create it.
if (!destinationDir.exists()) {
if (!destinationDir.mkdir()) {
// If we can't create the directory then fail
throw new NoModificationAllowedException("Couldn't create the destination direcotry");
}
}
for (File file : srcDir.listFiles()) {
if (file.isDirectory()) {
copyDirectory(file, destinationDir);
} else {
File destination = new File(destinationDir.getAbsoluteFile() + File.separator + file.getName());
copyFile(file, destination);
}
}
return getEntry(destinationDir);
}
/**
* Move a file
*
* @param srcFile file to be copied
* @param destFile destination to be copied to
* @return a FileEntry object
* @throws IOException
* @throws InvalidModificationException
* @throws JSONException
*/
private JSONObject moveFile(File srcFile, File destFile) throws JSONException, InvalidModificationException {
// Renaming a file to an existing directory should fail
if (destFile.exists() && destFile.isDirectory()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
// Try to rename the file
if (!srcFile.renameTo(destFile)) {
// Trying to rename the file failed. Possibly because we moved across file system on the device.
// Now we have to do things the hard way
// 1) Copy all the old file
// 2) delete the src file
}
return getEntry(destFile);
}
/**
* Move a directory
*
* @param srcDir directory to be copied
* @param destinationDir destination to be copied to
* @return a DirectoryEntry object
* @throws JSONException
* @throws IOException
* @throws NoModificationAllowedException
* @throws InvalidModificationException
*/
private JSONObject moveDirectory(File srcDir, File destinationDir) throws JSONException, FileExistsException, NoModificationAllowedException, InvalidModificationException {
// Renaming a file to an existing directory should fail
if (destinationDir.exists() && destinationDir.isFile()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
// Check to make sure we are not copying the directory into itself
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
throw new InvalidModificationException("Can't copy itself into itself");
}
// If the destination directory already exists and is empty then delete it. This is according to spec.
if (destinationDir.exists()) {
if (destinationDir.list().length > 0) {
throw new InvalidModificationException("directory is not empty");
}
}
// Try to rename the directory
if (!srcDir.renameTo(destinationDir)) {
// Trying to rename the directory failed. Possibly because we moved across file system on the device.
// Now we have to do things the hard way
// 1) Copy all the old files
// 2) delete the src directory
}
return getEntry(destinationDir);
}
/**
* Deletes a directory and all of its contents, if any. In the event of an error
* [e.g. trying to delete a directory that contains a file that cannot be removed],
* some of the contents of the directory may be deleted.
* It is an error to attempt to delete the root directory of a filesystem.
*
* @param filePath the directory to be removed
* @return a boolean representing success of failure
* @throws FileExistsException
*/
private boolean removeRecursively(String filePath) throws FileExistsException {
File fp = new File(filePath);
// You can't delete the root directory.
if (atRootDirectory(filePath)) {
return false;
}
return removeDirRecursively(fp);
}
/**
* Loops through a directory deleting all the files.
*
* @param directory to be removed
* @return a boolean representing success of failure
* @throws FileExistsException
*/
private boolean removeDirRecursively(File directory) throws FileExistsException {
if (directory.isDirectory()) {
for (File file : directory.listFiles()) {
removeDirRecursively(file);
}
}
if (!directory.delete()) {
throw new FileExistsException("could not delete: " + directory.getName());
} else {
return true;
}
}
/**
* Deletes a file or directory. It is an error to attempt to delete a directory that is not empty.
* It is an error to attempt to delete the root directory of a filesystem.
*
* @param filePath file or directory to be removed
* @return a boolean representing success of failure
* @throws NoModificationAllowedException
* @throws InvalidModificationException
*/
private boolean remove(String filePath) throws NoModificationAllowedException, InvalidModificationException {
File fp = new File(filePath);
// You can't delete the root directory.
if (atRootDirectory(filePath)) {
throw new NoModificationAllowedException("You can't delete the root directory");
}
// You can't delete a directory that is not empty
if (fp.isDirectory() && fp.list().length > 0) {
throw new InvalidModificationException("You can't delete a directory that is not empty.");
}
return fp.delete();
}
/**
* Creates or looks up a file.
*
* @param dirPath base directory
* @param fileName file/directory to lookup or create
* @param options specify whether to create or not
* @param directory if true look up directory, if false look up file
* @return a Entry object
* @throws FileExistsException
* @throws IOException
* @throws TypeMismatchException
* @throws EncodingException
* @throws JSONException
*/
private JSONObject getFile(String dirPath, String fileName, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
boolean create = false;
boolean exclusive = false;
if (options != null) {
create = options.optBoolean("create");
if (create) {
exclusive = options.optBoolean("exclusive");
}
}
// Check for a ":" character in the file to line up with BB and iOS
if (fileName.contains(":")) {
throw new EncodingException("This file has a : in it's name");
}
File fp = createFileObject(dirPath, fileName);
if (create) {
if (exclusive && fp.exists()) {
throw new FileExistsException("create/exclusive fails");
}
if (directory) {
fp.mkdir();
} else {
fp.createNewFile();
}
if (!fp.exists()) {
throw new FileExistsException("create fails");
}
}
else {
if (!fp.exists()) {
throw new FileNotFoundException("path does not exist");
}
if (directory) {
if (fp.isFile()) {
throw new TypeMismatchException("path doesn't exist or is file");
}
} else {
if (fp.isDirectory()) {
throw new TypeMismatchException("path doesn't exist or is directory");
}
}
}
// Return the directory
return getEntry(fp);
}
/**
* If the path starts with a '/' just return that file object. If not construct the file
* object from the path passed in and the file name.
*
* @param dirPath root directory
* @param fileName new file name
* @return
*/
private File createFileObject(String dirPath, String fileName) {
File fp = null;
if (fileName.startsWith("/")) {
fp = new File(fileName);
} else {
fp = new File(dirPath + File.separator + fileName);
}
return fp;
}
/**
* Look up the parent DirectoryEntry containing this Entry.
* If this Entry is the root of its filesystem, its parent is itself.
*
* @param filePath
* @return
* @throws JSONException
*/
private JSONObject getParent(String filePath) throws JSONException {
if (atRootDirectory(filePath)) {
return getEntry(filePath);
}
return getEntry(new File(filePath).getParent());
}
/**
* Checks to see if we are at the root directory. Useful since we are
* not allow to delete this directory.
*
* @param filePath to directory
* @return true if we are at the root, false otherwise.
*/
private boolean atRootDirectory(String filePath) {
if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache") ||
filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath())) {
return true;
}
return false;
}
/**
* Look up metadata about this entry.
*
* @param filePath to entry
* @return a Metadata object
* @throws FileNotFoundException
* @throws JSONException
*/
private JSONObject getMetadata(String filePath) throws FileNotFoundException, JSONException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException("Failed to find file in getMetadata");
}
JSONObject metadata = new JSONObject();
metadata.put("modificationTime", file.lastModified());
return metadata;
}
/**
* Returns a File that represents the current state of the file that this FileEntry represents.
*
* @param filePath to entry
* @return returns a JSONObject represent a W3C File object
* @throws FileNotFoundException
* @throws JSONException
*/
private JSONObject getFileMetadata(String filePath) throws FileNotFoundException, JSONException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException("File: " + filePath + " does not exist.");
}
JSONObject metadata = new JSONObject();
metadata.put("size", file.length());
metadata.put("type", getMimeType(filePath));
metadata.put("name", file.getName());
metadata.put("fullPath", file.getAbsolutePath());
metadata.put("lastModifiedDate", file.lastModified());
return metadata;
}
/**
* Requests a filesystem in which to store application data.
*
* @param type of file system requested
* @return a JSONObject representing the file system
* @throws IOException
* @throws JSONException
*/
private JSONObject requestFileSystem(int type) throws IOException, JSONException {
JSONObject fs = new JSONObject();
if (type == TEMPORARY) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
fs.put("name", "temporary");
fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() +
"/Android/data/" + ctx.getPackageName() + "/cache/"));
// Create the cache dir if it doesn't exist.
File fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
"/Android/data/" + ctx.getPackageName() + "/cache/");
fp.mkdirs();
} else {
throw new IOException("SD Card not mounted");
}
}
else if (type == PERSISTENT) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
fs.put("name", "persistent");
fs.put("root", getEntry(Environment.getExternalStorageDirectory()));
} else {
throw new IOException("SD Card not mounted");
}
}
else if (type == RESOURCE) {
fs.put("name", "resource");
}
else if (type == APPLICATION) {
fs.put("name", "application");
}
else {
throw new IOException("No filesystem of type requested");
}
return fs;
}
/**
* Returns a JSON Object representing a directory on the device's file system
*
* @param path to the directory
* @return
* @throws JSONException
*/
private JSONObject getEntry(File file) throws JSONException {
JSONObject entry = new JSONObject();
entry.put("isFile", file.isFile());
entry.put("isDirectory", file.isDirectory());
entry.put("name", file.getName());
entry.put("fullPath", file.getAbsolutePath());
// I can't add the next thing it as it would be an infinite loop
//entry.put("filesystem", null);
return entry;
}
/**
* Returns a JSON Object representing a directory on the device's file system
*
* @param path to the directory
* @return
* @throws JSONException
*/
private JSONObject getEntry(String path) throws JSONException {
return getEntry(new File(path));
}
/**
* Identifies if action to be executed returns a value and should be run synchronously.
*
* @param action The action to execute
* @return T=returns value
*/
public boolean isSynch(String action) {
public boolean isSynch(String action) {
if (action.equals("readAsText")) {
return false;
}
@ -176,6 +845,39 @@ public class FileUtils extends Plugin {
else if (action.equals("writeAsText")) {
return false;
}
else if (action.equals("requestFileSystem")) {
return false;
}
else if (action.equals("getMetadata")) {
return false;
}
else if (action.equals("toURI")) {
return false;
}
else if (action.equals("getParent")) {
return false;
}
else if (action.equals("getFile")) {
return false;
}
else if (action.equals("getDirectory")) {
return false;
}
else if (action.equals("remove")) {
return false;
}
else if (action.equals("removeRecursively")) {
return false;
}
else if (action.equals("readEntries")) {
return false;
}
else if (action.equals("getFileMetadata")) {
return false;
}
else if (action.equals("resolveLocalFileSystemURI")) {
return false;
}
return true;
}
@ -226,14 +928,24 @@ public class FileUtils extends Plugin {
contentType = this.ctx.getContentResolver().getType(fileUri);
}
else {
MimeTypeMap map = MimeTypeMap.getSingleton();
contentType = map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
contentType = getMimeType(filename);
}
byte[] base64 = Base64.encodeBase64(bos.toByteArray());
String data = "data:" + contentType + ";base64," + new String(base64);
return data;
}
/**
* Looks up the mime type of a given file name.
*
* @param filename
* @return a mime type
*/
private String getMimeType(String filename) {
MimeTypeMap map = MimeTypeMap.getSingleton();
return map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
}
/**
* Write contents of file.
@ -309,3 +1021,4 @@ public class FileUtils extends Plugin {
}
}
}

View File

@ -10,6 +10,8 @@ package com.phonegap.api;
import org.json.JSONArray;
import org.json.JSONObject;
import android.util.Log;
public class PluginResult {
private final int status;
private final String message;
@ -32,6 +34,12 @@ public class PluginResult {
this.cast = cast;
}
public PluginResult(Status status, JSONObject message, String cast) {
this.status = status.ordinal();
this.message = message.toString();
this.cast = cast;
}
public PluginResult(Status status, JSONArray message) {
this.status = status.ordinal();
this.message = message.toString();

View File

@ -0,0 +1,9 @@
package com.phonegap.file;
public class EncodingException extends Exception {
public EncodingException(String message) {
super(message);
}
}

View File

@ -0,0 +1,9 @@
package com.phonegap.file;
public class FileExistsException extends Exception {
public FileExistsException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,9 @@
package com.phonegap.file;
public class InvalidModificationException extends Exception {
public InvalidModificationException(String message) {
super(message);
}
}

View File

@ -0,0 +1,9 @@
package com.phonegap.file;
public class NoModificationAllowedException extends Exception {
public NoModificationAllowedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,9 @@
package com.phonegap.file;
public class TypeMismatchException extends Exception {
public TypeMismatchException(String message) {
super(message);
}
}