FileSystem APIとFile API:理解して使用する

パフォーマンスとアンプを備えたHTML5  統合、およびオフラインとアンプ。 保管

この記事では、FileSystem APIとFile APIを検討し、そのメソッドを理解し、いくつかの有用なことを示したいと思います。 この記事は、 html5rocks1、2、3 )を使用した資料の編集です。 以下のすべてのデモは、最初の2つのリンクで表示できます。 3番目のリンクでは、いくつかの興味深いデモも提供しています。 それでは、資料を調べてみましょう。



はじめに



FileSystem APIおよびFile APIを使用すると、 Webアプリケーションはユーザーのサンドボックス領域にあるファイルを作成、読み取り、表示、および書き込みできます。



APIの説明は、次のセクションに分かれています。



使用するオブジェクトに関するいくつかの言葉:

  1. ファイル-ファイル自体。 名前、サイズ、MIMEタイプなどの読み取り専用情報を取得できます。
  2. FileList-Fileオブジェクトの「配列」。
  3. Blob-バイト単位でファイルを解析できるエンティティ。


ブラウザのサポートとストレージの制限


執筆の時点では、Google Chrome 9+のみがFileSystem APIの実用的な実装を持っています。 現時点では、ファイルとストレージのクォータを管理するためのダイアログボックスはないので、 -unlimited-quota-for-filesフラグを使用する必要があります (Chromeウェブストア用のアプリケーションを開発する場合は、 unlimitedStorage権限を持つマニフェストで十分です)。 しかし、すべてが変化しており、ユーザーはすぐに、アプリケーションで必要となるファイルを操作する権利を管理する機会があります。



fileを使用してアプリケーションの借方に記入した場合、--allow-file-access-from-filesフラグを使用する必要がある場合があります 。 このフラグを使用しない場合、 SECURITY_ERRまたはQUOTA_EXCEEDED_ERRタイプの例外がスローされます。



ファイルシステムに訴える



必要な機能についてブラウザのサポートを確認してください

//   File API if (window.File && window.FileReader && window.FileList && window.Blob) { //  } else { alert('File API    '); }
      
      





Webアプリケーションは、次のwindow.requestFileSystem()メソッドを呼び出すことにより、ファイルシステムにアクセスできます(当然、限られた「サンドボックス」内)。

 window.requestFileSystem(type, size, successCallback, opt_errorCallback)
      
      





タイプ

ストレージルール、利用可能なwindow.TEMPORARYおよびwindow.PERSISTENT値TEMPORARYキーを使用して保存されたデータは、ブラウザの裁量で削除できます(たとえば、十分なスペースがない場合)。 PERSISTENTキーが設定されている場合、データはユーザーまたはアプリケーションのアクション後にのみクリアできます。



サイズ

アプリケーションが必要とするストレージのサイズ(バイト単位)。



successCallback

ファイルシステムへのアクセスに成功した場合に実行されるコールバック関数。 引数はFileSystem型のオブジェクトです。



opt_errorCallback

エラーを処理するためのオプションのコールバック関数。 また、ファイルシステムアクセスエラーが発生したときにも呼び出されます。 パラメーターは、タイプFileErrorのオブジェクトです。



アプリケーション内で初めてrequestFileSystem()を呼び出すと、その時点でリポジトリが作成されます。 このリポジトリは閉じられており、別のアプリケーションがそれにアクセスできないことを覚えておくことが非常に重要です。 また、アプリケーションがハードドライブにある他のファイルやフォルダーを変更できないことも意味します。



使用例

 function onInitFs(fs) { console.log('Opened file system: ' + fs.name); } window.requestFileSystem(window.PERSISTENT, 5*1024*1024 /*5MB*/, onInitFs, errorHandler);
      
      





FileSystem仕様には、同期操作用のAPI、つまりWebFilesが共有することになっているLocalFileSystemSyncインターフェースも記載されています。 ただし、このAPIはこの記事では考慮されません。



requestFileSystem()メソッドに戻って、発生する可能性のあるエラーのバリエーションを説明する価値があります。

 function errorHandler(e) { var msg = ''; switch (e.code) { case FileError.QUOTA_EXCEEDED_ERR: msg = 'QUOTA_EXCEEDED_ERR'; break; case FileError.NOT_FOUND_ERR: msg = 'NOT_FOUND_ERR'; break; case FileError.SECURITY_ERR: msg = 'SECURITY_ERR'; break; case FileError.INVALID_MODIFICATION_ERR: msg = 'INVALID_MODIFICATION_ERR'; break; case FileError.INVALID_STATE_ERR: msg = 'INVALID_STATE_ERR'; break; default: msg = 'Unknown Error'; break; }; console.log('Error: ' + msg); }
      
      





説明されている例は非常に単純ですが、本質的には、新たなエラーに対処するための準備です。



ファイルを操作する



ファイルを操作するために、FileEntryインターフェイスが提供されています。 通常のファイルとの関連付けに使用される多くのメソッドとプロパティがあります。 それらを以下に示します。

 fileEntry.isFile === true fileEntry.isDirectory === false fileEntry.name fileEntry.fullPath ... fileEntry.getMetadata(successCallback, opt_errorCallback); fileEntry.remove(successCallback, opt_errorCallback); fileEntry.moveTo(dirEntry, opt_newName, opt_successCallback, opt_errorCallback); fileEntry.copyTo(dirEntry, opt_newName, opt_successCallback, opt_errorCallback); fileEntry.getParent(successCallback, opt_errorCallback); fileEntry.toURI(opt_mimeType); // Currently not implemented in Google Chrome 9. fileEntry.file(successCallback, opt_errorCallback); fileEntry.createWriter(successCallback, opt_errorCallback); ...
      
      





FileEntryの操作の基本の例を見てみましょう。



ファイル作成


DirectoryEntryインターフェイスのgetFile()メソッドを使用して、ファイルを取得または作成できます。 リポジトリにアクセスした後、コールバックは、DirectoryEntry(fs.root)を含むFileSystemオブジェクトを返します。これは、リポジトリのルートフォルダーを参照します。



次のコードは、空のlog.txtファイルを作成します。

 function onInitFs(fs) { fs.root.getFile('log.txt', {create: true, exclusive: true}, function(fileEntry) { // fileEntry     // fileEntry.isFile === true // fileEntry.name == 'log.txt' // fileEntry.fullPath == '/log.txt' }, errorHandler); } window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler);
      
      





したがって、ファイルストレージにアクセスした後、私たちの手にあるのはFileSystemです。 コールバック関数内で、fs.root.getFile()メソッドを呼び出して、作成するファイルの名前を渡すことができます。 相対パスと絶対パスの両方を伝えることができます-主なことは、それが真実であることです。 たとえば、親フォルダーが存在しない場合、ファイルの作成はエラーになります。 getFile()メソッドの2番目の引数は、オブジェクトがまだ作成されていない場合に適用されるオブジェクトのパラメーターを記述するオブジェクトです。 詳細については、ドキュメントをご覧ください。



名前でファイルを読み込む


次のコードは、「log.txt」ファイルにアクセスし、FileReader APIを使用してその内容を読み取り、すべての内容を<textarea>ブロックに書き込みます。 ファイルが存在しない場合、エラーがスローされます。

 function onInitFs(fs) { fs.root.getFile('log.txt', {}, function(fileEntry) { fileEntry.file(function(file) { var reader = new FileReader(); reader.onloadend = function(e) { var txtArea = document.createElement('textarea'); txtArea.value = this.result; document.body.appendChild(txtArea); }; reader.readAsText(file); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler);
      
      





FileReaderは、とりわけ以下の読み取りメソッドを提供します。



次の例では、画像を読み取り、そのサムネイルを表示します。

 <style> .thumb { height: 75px; border: 1px solid #000; margin: 10px 5px 0 0; } </style> <input type="file" id="files" name="files[]" multiple /> <output id="list"></output> <script> function handleFileSelect(evt) { var files = evt.target.files; // FileList object // Loop through the FileList and render image files as thumbnails. for (var i = 0, f; f = files[i]; i++) { // Only process image files. if (!f.type.match('image.*')) { continue; } var reader = new FileReader(); // Closure to capture the file information. reader.onload = (function(theFile) { return function(e) { // Render thumbnail. var span = document.createElement('span'); span.innerHTML = ['<img class="thumb" src="', e.target.result, '" title="', theFile.name, '"/>'].join(''); document.getElementById('list').insertBefore(span, null); }; })(f); // Read in the image file as a data URL. reader.readAsDataURL(f); } } document.getElementById('files').addEventListener('change', handleFileSelect, false); </script>
      
      





ファイル全体ではなく、その一部のみが必要な場合があります。このため、File.slice(start_byte、length)を使用すると便利です。

次のようになります。

 var blob = file.slice(startingByte, length); reader.readAsBinaryString(blob);
      
      





次の例では、目的のバイトまたはファイル全体を読み取ることができます。 onloadendevt.target.readyStateに特に注意してください 。この場合、 onloadイベントが置き換えられます。 (以下のイベントについて)。

 <style> #byte_content { margin: 5px 0; max-height: 100px; overflow-y: auto; overflow-x: hidden; } #byte_range { margin-top: 5px; } </style> <input type="file" id="file" name="file" /> Read bytes: <span class="readBytesButtons"> <button data-startbyte="0" data-endbyte="4">1-5</button> <button data-startbyte="5" data-endbyte="14">6-15</button> <button data-startbyte="6" data-endbyte="7">7-8</button> <button>entire file</button> </span> <div id="byte_range"></div> <div id="byte_content"></div> <script> function readBlob(opt_startByte, opt_stopByte) { var files = document.getElementById('files').files; if (!files.length) { alert('Please select a file!'); return; } var file = files[0]; var start = opt_startByte || 0; var stop = opt_stopByte || file.size - 1; var reader = new FileReader(); // If we use onloadend, we need to check the readyState. reader.onloadend = function(evt) { if (evt.target.readyState == FileReader.DONE) { // DONE == 2 document.getElementById('byte_content').textContent = evt.target.result; document.getElementById('byte_range').textContent = ['Read bytes: ', start + 1, ' - ', stop + 1, ' of ', file.size, ' byte file'].join(''); } }; var length = (stop - start) + 1; var blob = file.slice(start, length); reader.readAsBinaryString(blob); } document.querySelector('.readBytesButtons').addEventListener('click', function(evt) { if (evt.target.tagName.toLowerCase() == 'button') { var startByte = evt.target.getAttribute('data-startbyte'); var endByte = evt.target.getAttribute('data-endbyte'); readBlob(startByte, endByte); } }, false); </script>
      
      





次にイベントについて。 FileReaderは、次のイベントタイプを提供します。

ファイルのアップロードプロセスを表示する必要がある場合に使用できます。 たとえば、次のように:

 <style> #progress_bar { margin: 10px 0; padding: 3px; border: 1px solid #000; font-size: 14px; clear: both; opacity: 0; -moz-transition: opacity 1s linear; -o-transition: opacity 1s linear; -webkit-transition: opacity 1s linear; } #progress_bar.loading { opacity: 1.0; } #progress_bar .percent { background-color: #99ccff; height: auto; width: 0; } </style> <input type="file" id="file" name="file" /> <button onclick="abortRead();">Cancel read</button> <div id="progress_bar"><div class="percent">0%</div></div> <script> var reader; var progress = document.querySelector('.percent'); function abortRead() { reader.abort(); } function errorHandler(evt) { switch(evt.target.error.code) { case evt.target.error.NOT_FOUND_ERR: alert('File Not Found!'); break; case evt.target.error.NOT_READABLE_ERR: alert('File is not readable'); break; case evt.target.error.ABORT_ERR: break; // noop default: alert('An error occurred reading this file.'); }; } function updateProgress(evt) { // evt is an ProgressEvent. if (evt.lengthComputable) { var percentLoaded = Math.round((evt.loaded / evt.total) * 100); // Increase the progress bar length. if (percentLoaded < 100) { progress.style.width = percentLoaded + '%'; progress.textContent = percentLoaded + '%'; } } } function handleFileSelect(evt) { // Reset progress indicator on new file selection. progress.style.width = '0%'; progress.textContent = '0%'; reader = new FileReader(); reader.onerror = errorHandler; reader.onprogress = updateProgress; reader.onabort = function(e) { alert('File read cancelled'); }; reader.onloadstart = function(e) { document.getElementById('progress_bar').className = 'loading'; }; reader.onload = function(e) { // Ensure that the progress bar displays 100% at the end. progress.style.width = '100%'; progress.textContent = '100%'; setTimeout("document.getElementById('progress_bar').className='';", 2000); } // Read in the image file as a binary string. reader.readAsBinaryString(evt.target.files[0]); } document.getElementById('files').addEventListener('change', handleFileSelect, false); </script>
      
      







ファイルに書き込む


次のコードを使用して、「log.txt」ファイル(存在しない場合)を作成し、「Ipsum Lorem」を書き込みます。

 function onInitFs(fs) { fs.root.getFile('log.txt', {create: true}, function(fileEntry) { fileEntry.createWriter(function(fileWriter) { fileWriter.onwriteend = function(e) { console.log('Write completed.'); }; fileWriter.onerror = function(e) { console.log('Write failed: ' + e.toString()); }; var bb = new BlobBuilder(); bb.append('Ipsum Lorem'); fileWriter.write(bb.getBlob('text/plain')); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler);
      
      





ご覧のとおり、 createWriter()メソッドを呼び出してオブジェクトを取得します。 さらに、ファイルへの書き込みの終了イベントと、エラーの発生の可能性を処理します。



ファイルにデータを追加します


次のコードは、ファイルの末尾に「Hello World」を追加します。 ファイルがない場合、エラーがスローされます。

 function onInitFs(fs) { fs.root.getFile('log.txt', {create: false}, function(fileEntry) { fileEntry.createWriter(function(fileWriter) { fileWriter.seek(fileWriter.length); // Start write position at EOF. var bb = new BlobBuilder(); bb.append('Hello World'); fileWriter.write(bb.getBlob('text/plain')); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler);
      
      







選択したファイルのコピーを作成する


次のコードにより、ユーザーは<input type = "file" multiple>を使用して複数のファイルを選択し、これらのファイルのコピーを作成できます。

 <input type="file" id="myfile" multiple />
      
      





 document.querySelector('#myfile').onchange = function(e) { var files = this.files; window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { for (var i = 0, file; file = files[i]; ++i) { (function(f) { fs.root.getFile(file.name, {create: true, exclusive: true}, function(fileEntry) { fileEntry.createWriter(function(fileWriter) { fileWriter.write(f); // Note: write() can take a File or Blob object. }, errorHandler); }, errorHandler); })(file); } }, errorHandler); };
      
      





ファイルの選択を簡単にするために、HTML5ドラッグアンドドロップを使用できます。



 <div id="drop_zone">Drop files here</div> <output id="list"></output> <script> function handleFileSelect(evt) { evt.stopPropagation(); evt.preventDefault(); var files = evt.dataTransfer.files; // FileList object. // files is a FileList of File objects. List some properties. var output = []; for (var i = 0, f; f = files[i]; i++) { output.push('<li><strong>', f.name, '</strong> (', f.type || 'n/a', ') - ', f.size, ' bytes, last modified: ', f.lastModifiedDate.toLocaleDateString(), '</li>'); } document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>'; } function handleDragOver(evt) { evt.stopPropagation(); evt.preventDefault(); } // Setup the dnd listeners. var dropZone = document.getElementById('drop_zone'); dropZone.addEventListener('dragover', handleDragOver, false); dropZone.addEventListener('drop', handleFileSelect, false); </script>
      
      







ファイルを削除する


次のコードは「log.txt」を削除します。

 window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { fs.root.getFile('log.txt', {create: false}, function(fileEntry) { fileEntry.remove(function() { console.log('File removed.'); }, errorHandler); }, errorHandler); }, errorHandler);
      
      







ディレクトリを操作する



ディレクトリの操作は、FileEntryのプロパティのほとんどを備えたDirectoryEntryを使用して実現されます(これらは両方ともEntryインターフェイスを継承します)。 以下にDirectoryEntryのプロパティとメソッドをリストします。

 dirEntry.isDirectory === true //      FileEntry ... var dirReader = dirEntry.createReader(successCallback, opt_errorCallback); dirEntry.getFile(path, opt_flags, opt_successCallback, opt_errorCallback); dirEntry.getDirectory(path, opt_flags, opt_successCallback, opt_errorCallback); dirEntry.removeRecursively(successCallback, opt_errorCallback); ...
      
      







ディレクトリの作成


ディレクトリを作成してアクセスするに 、DirectoryEntryインターフェイスのgetDirectory()を使用します。 名前とパスの両方をディレクトリに渡すことができます。



ルートに「MyPictures」ディレクトリを作成します。

 window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { fs.root.getDirectory('MyPictures', {create: true}, function(dirEntry) { ... }, errorHandler); }, errorHandler);
      
      







サブディレクトリ


サブディレクトリの作成は基本的にディレクトリの作成と同じですが、親ディレクトリが存在しない場合はエラーがスローされることに注意してください。 次のコードは、この制限を回避する方法を示しています。

 var path = 'music/genres/jazz/'; function createDir(rootDirEntry, folders) { // './'  '/' if (folders[0] == '.' || folders[0] == '') { folders = folders.slice(1); } rootDirEntry.getDirectory(folders[0], {create: true}, function(dirEntry) { if (folders.length) { createDir(dirEntry, folders.slice(1)); } }, errorHandler); }; function onInitFs(fs) { createDir(fs.root, path.split('/')); // fs.root is a DirectoryEntry. } window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler);
      
      





これでディレクトリ「music / genres / jazz」が作成されました。任意のレベルに移動して、その中に新しいファイルを作成できます。 例:

 window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { fs.root.getFile('/music/genres/jazz/song.mp3', {create: true}, function(fileEntry) { ... }, errorHandler); }, errorHandler);
      
      







ディレクトリの内容を解析します


ディレクトリ内の内容を確認するには、DirectoryReaderを作成してそのreadEntries()メソッドを呼び出す必要がありますが、選択したディレクトリの内容全体が返されるという保証はありません(!!!)。 つまり、結果が空になるまでDirectoryReader.readEntries()メソッドにアクセスする必要があります。 例:

 <ul id="filelist"></ul>
      
      





 function toArray(list) { return Array.prototype.slice.call(list || [], 0); } function listResults(entries) { var fragment = document.createDocumentFragment(); entries.forEach(function(entry, i) { var img = entry.isDirectory ? '<img src="folder-icon.gif">' : '<img src="file-icon.gif">'; var li = document.createElement('li'); li.innerHTML = [img, '<span>', entry.name, '</span>'].join(''); fragment.appendChild(li); }); document.querySelector('#filelist').appendChild(fragment); } function onInitFs(fs) { var dirReader = fs.root.createReader(); var entries = []; var readEntries = function() { dirReader.readEntries (function(results) { if (!results.length) { listResults(entries.sort()); } else { entries = entries.concat(toArray(results)); readEntries(); } }, errorHandler); }; readEntries(); } window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler);
      
      







ディレクトリを削除


削除するには、DirectoryEntry.remove()メソッドを呼び出します。 空でないディレクトリを削除しようとすると、エラーがスローされることを知っておくことが重要です。



「/ music / genres /」から空のjazzディレクトリを削除します。

 window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { fs.root.getDirectory('music/genres/jazz', {}, function(dirEntry) { dirEntry.remove(function() { console.log('Directory removed.'); }, errorHandler); }, errorHandler); }, errorHandler);
      
      







ディレクトリを再帰的に削除する


空でないディレクトリがあり、それでも削除したい場合は、 removeRecursively()メソッドが役立ちます。これにより、ディレクトリとそのすべてのコンテンツの両方が削除されます。



「music」ディレクトリでこの操作を実行します。

 window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { fs.root.getDirectory('/misc/../music', {}, function(dirEntry) { dirEntry.removeRecursively(function() { console.log('Directory removed.'); }, errorHandler); }, errorHandler); }, errorHandler);
      
      







コピー、名前変更、移動



FileEntryとDirectoryEntryは、この面では完全に同一です。



コピー


FileEntryとDirectoryEntryの両方に、コピー用のcopyTo()メソッドがあります。 ディレクトリの場合、メソッドはすべてのコンテンツを再帰的に作成します。



「me.png」をあるディレクトリから別のディレクトリにコピーします。

 function copy(cwd, src, dest) { cwd.getFile(src, {}, function(fileEntry) { cwd.getDirectory(dest, {}, function(dirEntry) { dirEntry.copyTo(dirEntry); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { copy(fs.root, '/folder1/me.png', 'folder2/mypics/'); }, errorHandler);
      
      







移動または名前変更


FileEntryとDirectoryEntryには、ファイルとディレクトリの移動と名前変更を可能にするmoveTo()メソッドがあります。 最初の引数は親フォルダー、2番目の(オプション)パラメーターは新しい名前です。



「me.png」の名前を「you.png」に変更します。

 function rename(cwd, src, newName) { cwd.getFile(src, {}, function(fileEntry) { fileEntry.moveTo(cwd, newName); }, errorHandler); } window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { rename(fs.root, 'me.png', 'you.png'); }, errorHandler);
      
      







「me.png」をルートディレクトリから「newfolder」に移動します。

 function move(src, dirName) { fs.root.getFile(src, {}, function(fileEntry) { fs.root.getDirectory(dirName, {}, function(dirEntry) { fileEntry.moveTo(dirEntry); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.PERSISTENT, 1024*1024, function(fs) { move('/me.png', 'newfolder/'); }, errorHandler);
      
      







ユースケース


HTML5にはローカルデータストレージのオプションがいくつかありますが、FileSystemはデータベースを満たさないニーズを満たすという点で異なります。 基本的に、これは大量のバイナリデータを保存するアプリケーションのニーズであり、ブラウザ外部のアプリケーションへのファイルアクセスを提供します。



可能な用途のリストは次のとおりです。

  1. アップローダーファイル
  2. メディアファイルを操作するゲームとアプリケーション
  3. オフラインモード付きのオーディオ/写真エディター
  4. オフラインビデオプレーヤー
  5. オフラインメールクライアント



All Articles