非标
This feature is not on a current W3C standards track, but it is supported on the Firefox OS platform. Although implementations may change in the future and it is not supported widely across browsers, it is suitable for use in code dedicated to Firefox OS apps.
This API is available on Firefox OS for privileged or certified applications 仅。
The Device Storage API is used to access the file system within a Web app. Accessing the file system can be highly sensitive, and for that reason this API is available for privileged apps only.
注意: Accessing device storage is slow due to a limitation at the physical level. In many cases it can be faster to use an IndexedDB database to store files instead of physical device storage.
This section explains what is needed to access device storage.
It's possible to access storage areas by using the
navigator.getDeviceStorage()
and
navigator.getDeviceStorages()
方法:
navigator.getDeviceStorage()
accepts a string parameter representing the name of the default storage area to be accessed. The method returns a
DeviceStorage
object, which is used to access the related storage area. It returns the storage area whose
.default
属性为
true
. This is controlled by the user via
Settings App > Media Storage > Default media location
.
navigator.getDeviceStorages()
on the other hand accepts a string representing the name of the storage area to be accessed, and returns an
数组
of
DeviceStorage
objects, one per physical storage area.
Firefox OS provides the following storage names:
apps
: This storage area is used to store the user data needed by apps. As it is critical data, accessing this storage area requires some extra privileges (see below) and is available to certified applications only.
music
: This is the storage area where music and sounds are stored.
pictures
: This is the storage area where pictures are stored.
sdcard
: This is the storage area that grants access to the device's SDCard.
sdcard
is also used as the name of the device's default internal storage, which is a bit unfortunate and potentially confusing; be aware.
videos
: This is the storage area where videos are stored.
var pics = navigator.getDeviceStorage('pictures');
当使用
navigator.getDeviceStorages()
, if there is more than one storage area then the internal one will be named for example
sdcard
and the physical storage area will be called something else (sometimes it's
extsdcard
, sometimes it's
sdcard1
). This varies by device manufacturer. The names of files on the
sdcard
storage area will be
/sdcard/path/filename
, and the names of files on the
sdcard1
storage area will be
/sdcard1/path/filename
, or whatever.
注意,
/sdcard
and
/sdcard1
are storage names. Their actual mount points on the system are determined via
vold
and/or
/system/etc/volume.cfg
file.) DeviceStorage transparently maps the
storageName
into the actual
mountPoint
(so you don't need the mount point if you're just accessing the files through device storage).
If you want to determine the mount point to examine the filesystem from an adb shell, then you can determine the
vold
mount points by using the command
adb shell vdc volume list
(this requires a root shell).
在 Flame , you'll see something like this:
110 0 sdcard /storage/sdcard 4
110 0 sdcard1 /storage/sdcard1 4
200 0 Volumes listed.
For volumes that aren't managed by
vold
(for example, the
sdcard
volume on a Nexus 4/5), the mount point is found in
/system/etc/volume.cfg
.
注意
: In Gaia engineering builds there is a
ds-test
app
, which is useful for device storage testing.
To be able to use these storage areas, the application must declare them in its application manifest. For example, if the application wants to access the
sdcard
storage area, it must have the "
device-storage:sdcard
" permission in its manifest.
"permissions": {
"device-storage:videos":{ "access": "readonly" },
"device-storage:pictures":{ "access": "readwrite" }
}
As mentioned above, using
device-storage:apps
also needs some extra permissions, in the form of the
webapps-manage
permission, which allows access to the
navigator.mozApps.mgmt
API for managing installed open web apps.
"permissions": {
"device-storage:apps":{ "access": "readwrite" },
"webapps-manage":{ }
}
All of the
device-storage
name permissions are privileged level, except for
apps
, which is certified.
webapps-manage
is certified level.
Once an application gets access to a storage area, it's possible to add, get and remove files inside the area.
Adding a file is done using the
addNamed
or
add
methods. The former allows to set an explicit name when storing a file while the latter creates a name automatically when the file is stored. Both methods are asynchronous and return a
DOMRequest
object to handle the
success
or
error
of the operation. This is very important as writing and reading files on a physical support is a slow process.
Those two methods expect a
Blob
as their first parameter. This object will be turned into a file under the hood and stored. When creating a
Blob
object, it's mandatory to give it a
type
。此
type
, which is a MIME type, is important because some storage areas have restrictions based on the type:
music
only accepts
Blob
with a valid audio MIME type
pictures
only accepts
Blob
with a valid image MIME type
videos
only accepts
Blob
with a valid video MIME type
var sdcard = navigator.getDeviceStorage("sdcard");
var file = new Blob(["This is a text file."], {type: "text/plain"});
var request = sdcard.addNamed(file, "my-file.txt");
request.onsuccess = function () {
var name = this.result;
console.log('File "' + name + '" successfully wrote on the sdcard storage area');
}
// An error typically occur if a file with the same name already exist
request.onerror = function () {
console.warn('Unable to write the file: ' + this.error);
}
注意:
Repositories in a storage area are implicit. It's not possible to create explicitly an empty repository. If you want to use a repository structure you have to make it part of the name of the file to store. So if you want to store the file
bar
在
foo
repository, you have to use the
addNamed
method with the complete path name of the file
addNamed(
blob
, "foo/bar")
. This is also true when you want to retrieve a file using its name (see below).
As files are added in a given restricted storage area for security reasons, a file path name cannot start with "
/
" nor "
../
" (and "
./
" is pointless).
Retrieving a file can be done in two ways: by using its name or by iterating the whole list of files.
The easiest way is to retrieve a file by its name using the
get
and
getEditable
methods. The former provides a
File
object (which act like a read only file) when the latter provides a
FileHandle
object (which allows updating the underlaying file). Both methods are asynchronous and return a
DOMRequest
object to handle the
success
or
error
of the operation.
var sdcard = navigator.getDeviceStorage('sdcard');
var request = sdcard.get("my-file.txt");
request.onsuccess = function () {
var file = this.result;
console.log("Get the file: " + file.name);
}
request.onerror = function () {
console.warn("Unable to get the file: " + this.error);
}
The other way to retrieve files is by browsing the content of the storage area. This is possible using the
enumerate
and
enumerateEditable
methods. The former provides
File
objects when the latter provides
FileHandle
objects. Both methods are asynchronous and return a
DOMCursor
object to iterate along the list of files. A
DOMCursor
is nothing less than a
DOMRequest
with extra power to iterate asynchronously along a list of things (files in that case).
var pics = navigator.getDeviceStorage('pictures');
// Let's browse all the images available
var cursor = pics.enumerate();
cursor.onsuccess = function () {
if(cursor.result.name !== null) {
var file = cursor.result;
console.log("File found: " + file.name);
// Once we found a file we check if there is other results
// Then we move to the next result, which call the cursor
// success with the next file as result.
this.continue();
}
}
cursor.onerror = function () {
console.warn("No file found: " + this.error);
}
It's possible to limit the number of results by passing two optional parameters to the
enumerate
and
enumerateEditable
方法。
since
property, which allows you to limit the search to a given time period.
var pics = navigator.getDeviceStorage('pictures');
// Lets retrieve picture from the last week.
var param = {
since: new Date((+new Date()) - 7*24*60*60*1000)
}
var cursor = pics.enumerate(param);
cursor.onsuccess = function () {
var file = this.result;
console.log("Picture taken on: " + file.lastModifiedDate);
if (!this.done) {
this.continue();
}
}
A file can be removed from the storage area by simply using the
delete
method. This method just needs the name of the file to delete. As all the other methods from the
DeviceStorage
interface, this one is also asynchronous and returns a
DOMRequest
object to handle the
success
or
error
of the operation.
var sdcard = navigator.getDeviceStorage('sdcard');
var request = sdcard.delete("my-file.txt");
request.onsuccess = function () {
console.log("File deleted");
}
request.onerror = function () {
console.log("Unable to delete the file: " + this.error);
}
Beyond accessing files, a storage area provides a few methods to easily reach some important information
One of the most important things to know when storing files on a device is the amount of space available. The
DeviceStorage
interface provides two useful functions dedicated to space:
freeSpace()
to get the amount of free space available to store new files;
usedSpace()
to get the amount of space used to store the files;
As those methods are asynchronous, they return a
DOMRequest
object to handle the
success
or
error
of the operation.
var videos = navigator.getDeviceStorage('videos');
var request = videos.usedSpace();
request.onsuccess = function () {
// The result is express in bytes, lets turn it into megabytes
var size = this.result / 1048576;
console.log("The videos on your device use a total of " + size.toFixed(2) + "Mo of space.");
}
request.onerror = function () {
console.warn("Unable to get the space used by videos: " + this.error);
}
As many applications can use a same storage area at the same time, it's sometimes useful for an application to be aware of a change in that storage area. It's also useful for an application performing an asynchronous action because it doesn't have to relay on all the
DOMRequest
objects returned by each method of the
DeviceStorage
接口。
To that end, a
change
event is triggered each time a file is created, modified or deleted. This event can be captured using the
onchange
特性或
addEventListener()
method. The event handler gets a
DeviceStorageChangeEvent
object which is a regular
事件
object with two extra properties:
DeviceStorageChangeEvent.reason
which gives the reason of the change (
created
,
modified
or
deleted
).
DeviceStorageChangeEvent.path
which gives the full path to the file affected by the change.
var sdcard = navigator.getDeviceStorage('sdcard');
sdcard.onchange = function (change) {
var reason = change.reason;
var path = change.path;
console.log('The file "' + path + '" has been ' + reason);
}
Not part of any specification.
Supported in Firefox OS 1.1.
navigator.getDeviceStorage()
navigator.getDeviceStorages()
DeviceStorage
DeviceStorageChangeEvent