タイトル: File API
SEOタイトル: Web File API 完全ガイド|File/Blob/FileReader/Drag&Drop/File System Access
| この記事の要点 |
|
File API とは
File API は、ブラウザ JavaScript からローカルファイルを安全に読み書きするための W3C / WHATWG 標準 API 群です。同一オリジンポリシーやユーザー操作(クリック・ドロップ)を起点とする原則により、ユーザーの明示的な許可なしに任意ファイルにアクセスできない設計です。
関連仕様: File API / FileReader API / HTML5 Drag and Drop API / File System Access API / Streams API。
基本オブジェクト
| オブジェクト | 説明 |
|---|---|
Blob | 任意バイナリデータ。サイズ・MIME を持つ |
File | Blob の拡張、ファイル名・最終更新日を追加 |
FileList | File の配列 (input.files / DataTransfer.files) |
FileReader | ファイルを非同期で読み取るリーダ |
URL.createObjectURL | Blob/File を一時 URL に変換 (img/video 等で使用) |
FormData | multipart/form-data として fetch でアップロード |
ファイル選択 (input type=file)
| 属性 | 説明 |
|---|---|
accept | MIME や拡張子で制限 (例: image/*,.pdf) |
multiple | 複数選択許可 |
capture | モバイルでカメラ起動 (user / environment) |
webkitdirectory | ディレクトリ単位選択 (Chromium/Firefox) |
FileReader で内容を読む
const reader = new FileReader();
// テキストとして読む
reader.onload = () => console.log(reader.result);
reader.readAsText(file, 'UTF-8');
// データ URL (Base64) として読む → img.src へ
reader.onload = () => document.getElementById('img').src = reader.result;
reader.readAsDataURL(file);
// ArrayBuffer として読む (バイナリ操作)
reader.onload = () => {
const buf = reader.result;
const view = new Uint8Array(buf);
console.log('first 4 bytes:', view.slice(0, 4));
};
reader.readAsArrayBuffer(file);
// 進捗・エラー
reader.onprogress = (e) => {
if (e.lengthComputable) console.log((e.loaded / e.total * 100).toFixed(1) + '%');
};
reader.onerror = () => console.error(reader.error);
// 中断
reader.abort();
モダンな書き方 (await + File メソッド)
// File は自身にメソッドを持つ (modern browsers)
const text = await file.text();
const buffer = await file.arrayBuffer();
const stream = file.stream(); // ReadableStream
// 巨大ファイルをチャンク処理
const reader = file.stream().getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log('chunk', value.byteLength);
}
URL.createObjectURL で表示
const url = URL.createObjectURL(file);
document.getElementById('preview').src = url;
// 不要になったら解放 (メモリリーク防止)
img.onload = () => URL.revokeObjectURL(url);
Drag & Drop
ここにファイルをドロップ
クリップボード経由
// 貼り付けイベントから画像取得
document.addEventListener('paste', async (e) => {
for (const item of e.clipboardData.items) {
if (item.type.startsWith('image/')) {
const file = item.getAsFile();
console.log('pasted image:', file.name, file.size);
}
}
});
// クリップボードに書き込み (要 user gesture)
const blob = new Blob(['hello'], { type: 'text/plain' });
await navigator.clipboard.write([new ClipboardItem({ 'text/plain': blob })]);
fetch でアップロード
// FormData (multipart)
const fd = new FormData();
fd.append('upload', file, file.name);
fd.append('title', 'sample');
const res = await fetch('/api/upload', { method: 'POST', body: fd });
// Blob を直接 PUT
await fetch('/api/upload', {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type },
});
// チャンク分割アップロード
const CHUNK = 5 * 1024 * 1024; // 5MB
for (let i = 0; i < file.size; i += CHUNK) {
const chunk = file.slice(i, i + CHUNK);
await fetch('/api/upload-chunk?offset=' + i, { method: 'POST', body: chunk });
}
ダウンロード (Blob 経由)
function downloadBlob(blob, filename) {
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
}
// CSV をその場で生成してダウンロード
const csv = 'name,score\nAlice,80\nBob,75';
downloadBlob(new Blob([csv], { type: 'text/csv' }), 'scores.csv');
File System Access API (Chrome 系)
従来の File API が読み取り中心なのに対し、File System Access API はユーザーの許可下でローカルファイル/ディレクトリへの書き込みも可能にします (Chromium 系のみ)。
// ファイルを開いて読み書き
const [handle] = await window.showOpenFilePicker({
types: [{ description: 'Text', accept: { 'text/plain': ['.txt'] } }],
});
const file = await handle.getFile();
console.log(await file.text());
// 同じ場所に書き戻し
const w = await handle.createWritable();
await w.write('updated content');
await w.close();
// ディレクトリ選択
const dir = await window.showDirectoryPicker();
for await (const [name, h] of dir.entries()) {
console.log(name, h.kind); // 'file' or 'directory'
}
// 新規ファイル保存
const saveHandle = await window.showSaveFilePicker({
suggestedName: 'note.txt',
types: [{ accept: { 'text/plain': ['.txt'] } }],
});
const ws = await saveHandle.createWritable();
await ws.write('hello');
await ws.close();
IndexedDB との連携
Blob は IndexedDB に直接格納できるため、オフラインキャッシュやローカル DB と組み合わせやすい:
const db = await openDB('files', 1, {
upgrade(db) { db.createObjectStore('store', { keyPath: 'name' }); },
});
await db.put('store', { name: file.name, blob: file });
const saved = await db.get('store', 'photo.jpg');
const url = URL.createObjectURL(saved.blob);
セキュリティ・制限事項
- 同一オリジンポリシー: ドラッグ元クロスオリジンの blob URL は使えないことがある
- ユーザー操作起点: showOpenFilePicker / showSaveFilePicker はクリックなど gesture 内でのみ呼び出し可能
- サイズ制限: ブラウザの IndexedDB / メモリ制限。大容量は Streams API でチャンク処理
- ファイル名は安全に: アップロード後はサーバ側で正規化 (拡張子強制、ハッシュ名)
ブラウザ対応
| API | Chrome | Firefox | Safari |
|---|---|---|---|
| FileReader / Blob / File | ○ | ○ | ○ |
| file.text() / arrayBuffer() | ○ | ○ | ○ |
| Drag & Drop | ○ | ○ | ○ |
| Clipboard image paste | ○ | ○ | ○ |
| File System Access | ○ | × | × |
| webkitdirectory | ○ | ○ | △ |
FAQ
Q: Base64 (DataURL) と Blob URL、どちらを使うべき?
A: 表示用には Blob URL。Base64 はメモリ・転送効率が悪い (約 33% 増)。サーバへ JSON 形式で送る必要があるときだけ Base64。
Q: ローカルに自由に書き込めないの?
A: 任意の場所は不可。File System Access API でユーザー許可を得た範囲のみ可能。それ以外は IndexedDB / OPFS (Origin Private File System) を使う。
Q: 巨大ファイル (1GB 超) をアップロードしたい
A: file.slice() でチャンク化し、サーバ側で結合。S3 マルチパートアップロードや tus プロトコルを使うと冪等で堅牢。