1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
---
title: FileHandle API
slug: Web/API/File_Handle_API
translation_of: Web/API/File_Handle_API
---
<p>{{non-standard_header}}</p>
<p>FileHandle API允许处理文件,包括创建文件和修改其内容(unlike the <a href="/en-US/docs/DOM/File" title="DOM/File">File API</a>)。由于通过该API操作的文件可以物理存储在设备上,因此编辑部分使用基于回合的锁定机制,以避免出现种族问题。</p>
<h2 id="API概述">API概述</h2>
<p>该API基于以下接口:</p>
<ul>
<li>{{domxref("IDBDatabase.mozCreateFileHandle")}} (was called {{domxref("IDBDatabase.mozCreateFileHandle")}}.)</li>
<li>{{domxref("IDBMutableFile")}} (was previously {{domxref("FileHandle")}}.)</li>
<li>{{domxref("LockedFile")}}</li>
<li>{{domxref("FileRequest")}}</li>
</ul>
<p>它还与File API,尤其是 {{domxref("File")}} 和{{domxref("Blob")}} 接口具有连接。</p>
<h2 id="Basic_operations">Basic operations</h2>
<h3 id="创建文件句柄">创建文件句柄</h3>
<p>因为目的是允许通过IndexedDB存储文件,所以创建{{domxref("FileHandle")}}实例需要<a href="/en-US/docs/IndexedDB/IDBFactory#open" title="IndexedDB/IDBFactory#open">IndexedDB Database</a>。</p>
<div style="overflow: hidden;">
<pre class="brush: js notranslate">var IDBReq = indexedDB.open("myFileStorageDataBase");
IDBReq.onsuccess = function(){
var DB = this.result;
var buildHandle = DB.mozCreateFileHandle("test.txt", "plain/text");
buildHandle.onsuccess = function(){
var myFileHandle = this.result;
console.log('handle', myFileHandle);
};
};
</pre>
</div>
<p>{{domxref("IDBDatabase.mozCreateFileHandle","mozCreateFileHandle()")}}接受两个参数:名称和可选类型。 这两个都是描述性的,数据库不使用。 但是,它们对于{{domxref("FileHandle")}}对象很重要,因为它可以生成{{domxref("File")}}对象,这些对象继承自己的{{domxref("File.name","name")}}和{{domxref("File.type","type")}}的值。 就是说,由于名称与任何实际文件名都不匹配,因此它可以是一个空字符串,甚至不必是唯一的。</p>
<div class="note">
<p><strong>Note:</strong> the above code only creates a "temporary file" that exists only while you hold the {{domxref("FileHandle")}} instance. If you want a file to survive a page refresh/app relaunch, you need to store the handle in a more permanent location, like the database itself. See {{Anch("File storage")}} below to learn more about this.</p>
</div>
<h3 id="执行读写操作">执行读写操作</h3>
<p>要在已处理的文件中进行读取或写入,需要获取{{domxref("LockedFile")}}。 {{domxref("FileHandle.open()")}} 方法提供了这样的对象,该对象可以是<code>readonly</code>的也可以是<code>readwrite</code>的。 对<code>readonly</code>{{domxref("LockedFile")}} 对象执行写操作的任何尝试都将失败。</p>
<h4 id="Writing">Writing</h4>
<p>There are three possible writing operations on a locked file:</p>
<ul>
<li>{{domxref("LockedFile.write","write")}} : It's an arbitrary writing method which starts writing in the file at the {{domxref("LockedFile.location")}} byte.</li>
<li>{{domxref("LockedFile.append","append")}} : This operation always writes content at the end of the file.</li>
<li>{{domxref("LockedFile.truncate","truncate")}} : This operation keeps the nth-first bytes of the file and removes the rest.</li>
</ul>
<pre class="brush: js notranslate">// Get a LockedFile object from the handle
var myFile = myFileHandle.open('readwrite');
// Start a writing operation
var writing = myFile.append('Some content');
writing.onsuccess = function () {
console.log('Writing operation successful');
}
writing.onerror = function () {
console.log('Something goes wrong in the writing process: ' + this.error);
}
</pre>
<h4 id="Reading">Reading</h4>
<p>可以直接写入{{domxref("LockedFile")}} 对象的内容,而无需使用中间的{{domxref("File")}}对象和 {{domxref("FileReader")}}对象。 {{domxref("LockedFile")}}接口提供了{{domxref("LockedFile.readAsText","readAsText")}}方法和{{domxref("LockedFile.readAsArrayBuffer","readAsArrayBuffer")}}方法 。</p>
<p>这两种方法期望一个大小来指示必须从{{domxref("LockedFile.location")}}字节开始读取多少个字节。</p>
<p>要读取整个文件,需要知道其大小。 可以通过{{domxref("LockedFile.getMetadata()")}}方法来检索此信息(及其最后修改日期)。</p>
<pre class="brush: js notranslate">// Get a LockedFile object from the handle
var myFile = myFileHandle.open('readwrite');
// Retrieve the size of the file
var getmeta = myFile.getMetadata();
getmeta.onsuccess = function () {
var size = this.result.size;
// The reading operation will start with the byte at index 0 in the file
myFile.location = 0;
// Start a reading operation for the whole file content
var reading = myFile.readAsText(size);
reading.onsuccess = function () {
console.log('The content of the file is:');
console.log(this.result);
}
reading.onerror = function () {
console.log('Something goes wrong in the reading process: ' + this.error);
}
}
</pre>
<h3 id="文件快照">文件快照</h3>
<p>在许多情况下,获取文件快照可能很方便。 例如,许多API都期望使用{{domxref("Blob")}}或{{domxref("File")}}对象,例如{{domxref("FileReader")}}(易于使用) 读取整个文件)或{{domxref("XMLHttpRequest")}}。</p>
<p>可以使用{{domxref("FileHandle.getFile","getFile")}}获得一个{{domxref("File")}}对象,该对象代表{{domxref("FileHandle")}}对象处理的文件的当前状态方法。 这样的{{domxref("File")}}对象与原始文件完全不同步,这意味着对该对象所做的任何更改将永远不会反映到已处理文件中,并且对已处理文件所做的任何更改也将永远不会被反映。 推送到{{domxref("File")}}对象。</p>
<pre class="brush: js notranslate">var mySnapshot = null;
var request = myFileHandle.getFile();
request.onsuccess = function () {
mySnapshot = this.result;
}
</pre>
<h3 id="管理进度">管理进度</h3>
<p>来自{{domxref("LockedFile")}}接口的所有方法都返回一个{{domxref("FileRequest")}}对象。 这样的对象基本上是具有额外功能的{{domxref("DOMRequest")}}:它可以监视操作的进度。 有时读写操作可能会很长,因此最好监视该操作以向用户提供反馈。 可以使用{{domxref("FileRequest.onprogress")}}事件处理程序来完成此类监视。</p>
<pre class="brush: js notranslate">var progress = document.querySelector('progress');
var myFile = myFileHandle.open('readonly');
// Let's read a 1GB file
var action = myFile.readAsArrayBuffer(1000000000);
action.onprogress = function (event) {
if (progress) {
progress.value = event.loaded;
progress.max = event.total;
}
}
action.onsuccess = function () {
console.log('Yeah \o/ Just read a 1GB file');
}
action.onerror = function () {
console.log('Oups :( Unable to read a 1GB file')
}
</pre>
<h2 id="文件储存">文件储存</h2>
<p>创建文件句柄时,只要您持有{{domxref("FileHandle")}}实例,关联文件就仅作为“临时文件”存在。 如果您希望文件在页面刷新/应用重新启动后仍然存在,则需要将句柄存储在数据库中(不一定是用于创建{{domxref("FileHandle")}} object)。</p>
<pre class="brush: js notranslate">var IDBReq = window.indexedDB.open('myFileStorageDataBase');
// If necessary, let's create a datastore for the files
IDBReq.onupgradeneeded = function () {
this.result.createObjectStore('files');
}
IDBReq.onsuccess = function () {
var DB = this.result;
// Let's create a new file
var handleReq = DB.mozCreateFileHandle("test.txt", "plain/text");
handleReq.onsuccess = function () {
var myFileHandle = this.result;
var store = DB.transaction(['files'], 'readwrite').objectStore('files');
// Let's store the file permanently
// HINT: it could be handy to use the file name as the storage key
var storeReq = store.add(myFileHandle, myFileHandle.name);
storeReq.onsuccess = function () {
console.log('The file has been successfully stored and can be retrieved anytime.')
}
}
}
</pre>
<p>以这种方式存储的文件将物理地放置在设备上。数据库本身仅存储指向该文件的指针。这意味着,如果{{domxref("FileHandle")}} 对象在多个DB或多个数据存储中存储了几次,则所有这些对象都将引用同一个唯一文件。这不是问题,因为要访问文件,需要一个{domxref("LockedFile")}}对象,并且对该对象的操作是独立执行的,这意味着一旦{{domxref("LockedFile")}} 在激活状态下,保证此 {{domxref("LockedFile")}} 的所有操作在基础文件上顺序发生,而不会与其他{{domxref("LockedFile")}}的操作交错。</p>
<h3 id="安全的写操作">安全的写操作</h3>
<p>出于性能原因,写(和读)操作是在内存中完成的。这些操作的结果定期异步刷新到设备存储区域。如果由于某种原因在此之前出现问题,则可能会丢失某些操作的结果。为了避免这个问题,可以使用 {{domxref("LockedFile.flush()")}} 方法。成功调用此方法后,可以确保对文件的更改是安全的。</p>
<pre class="brush: js notranslate">// Get a LockedFile object from the handle
var myFile = myFileHandle.open('readwrite');
// Start a writing operation
var writing = myFile.append('Some content');
writing.onsuccess = function () {
console.log('Writing operation successful');
var saving = myFile.flush();
saving.onsuccess = function () {
console.log('The file has been successfully stored');
}
}
writing.onerror = function () {
console.log('Something goes wrong in the writing process: ' + this.error);
}</pre>
<h2 id="API兼容性">API兼容性</h2>
<h3 id="Why_a_different_API_than_FileWriter">Why a different API than FileWriter?</h3>
<p>The <a href="http://dev.w3.org/2009/dap/file-system/file-writer.html" title="http://dev.w3.org/2009/dap/file-system/file-writer.html">FileWriter specification</a> defines FileWriters, objects aiming at representing editable files. <a href="http://lists.w3.org/Archives/Public/public-webapps/2012JanMar/0886.html" title="http://lists.w3.org/Archives/Public/public-webapps/2012JanMar/0886.html">Discussions on public-webapps</a> led to the conclusion that the API would behave poorly in the case of different entities writing concurrently to the same file. The outcome of this discussion is the FileHandle API with its LockedFile and transaction mechanism.</p>
<h2 id="Specifications">Specifications</h2>
<p>A formal specification draft is being written. As it does not fully match the current implementation, be warned that the implementation and/or the specification will be subject to changes.</p>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td>{{SpecName('FileSystem')}}</td>
<td>{{Spec2('FileSystem')}}</td>
<td>Draft proposal</td>
</tr>
</tbody>
</table>
<h2 id="Browser_compatibility">Browser compatibility</h2>
<div>{{CompatibilityTable}}</div>
<div id="compat-desktop">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Chrome</th>
<th>Firefox (Gecko)</th>
<th>Internet Explorer</th>
<th>Opera</th>
<th>Safari</th>
</tr>
<tr>
<td>Basic support</td>
<td>{{CompatNo}}</td>
<td>15</td>
<td>{{CompatNo}}</td>
<td>{{CompatNo}}</td>
<td>{{CompatNo}}</td>
</tr>
</tbody>
</table>
</div>
<div id="compat-mobile">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Android</th>
<th>Firefox Mobile (Gecko)</th>
<th>IE Mobile</th>
<th>Opera Mobile</th>
<th>Safari Mobile</th>
</tr>
<tr>
<td>Basic support</td>
<td>{{CompatNo}}</td>
<td>15</td>
<td>{{CompatNo}}</td>
<td>{{CompatNo}}</td>
<td>{{CompatNo}}</td>
</tr>
</tbody>
</table>
</div>
|