この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:07:02

タイトル: File API
SEOタイトル: Web File API 完全ガイド|File/Blob/FileReader/Drag&Drop/File System Access

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>File API</strong> はブラウザ上でローカルファイルを安全に扱う Web 標準</li><li>主要オブジェクト: <code>File</code> / <code>FileList</code> / <code>Blob</code> / <code>FileReader</code> / <code>URL.createObjectURL</code></li><li>読み取り: <code>readAsText</code> / <code>readAsDataURL</code> / <code>readAsArrayBuffer</code></li><li>入力: <code><input type="file"></code> / <strong>Drag & Drop</strong> / <strong>Clipboard</strong></li><li>モダン: <strong>File System Access API</strong> (Chrome) で読み書き可能、<strong>IndexedDB</strong> と連携</li></ul></td></tr></table>
<h2>File API とは</h2>

<p><strong>File API</strong> は、ブラウザ JavaScript からローカルファイルを<strong>安全に読み書き</strong>するための W3C / WHATWG 標準 API 群です。同一オリジンポリシーやユーザー操作(クリック・ドロップ)を起点とする原則により、ユーザーの明示的な許可なしに任意ファイルにアクセスできない設計です。</p>

<p>関連仕様: <strong>File API</strong> / <strong>FileReader API</strong> / <strong>HTML5 Drag and Drop API</strong> / <strong>File System Access API</strong> / <strong>Streams API</strong>。</p>

<h2>基本オブジェクト</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>オブジェクト</th><th>説明</th></tr></thead>
<tbody>
<tr><td><code>Blob</code></td><td>任意バイナリデータ。サイズ・MIME を持つ</td></tr>
<tr><td><code>File</code></td><td>Blob の拡張、ファイル名・最終更新日を追加</td></tr>
<tr><td><code>FileList</code></td><td>File の配列 (input.files / DataTransfer.files)</td></tr>
<tr><td><code>FileReader</code></td><td>ファイルを非同期で読み取るリーダ</td></tr>
<tr><td><code>URL.createObjectURL</code></td><td>Blob/File を一時 URL に変換 (img/video 等で使用)</td></tr>
<tr><td><code>FormData</code></td><td>multipart/form-data として fetch でアップロード</td></tr>
</tbody>
</table>

<h2>ファイル選択 (input type=file)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><input type="file" id="picker" accept="image/*" multiple>
<script>
const picker = document.getElementById(&apos;picker&apos;);
picker.addEventListener(&apos;change&apos;, (e) => {
  const files = e.target.files;       // FileList
  for (const file of files) {
    console.log(file.name, file.size, file.type, file.lastModified);
  }
});
</script></code></pre>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>属性</th><th>説明</th></tr></thead>
<tbody>
<tr><td><code>accept</code></td><td>MIME や拡張子で制限 (例: <code>image/*,.pdf</code>)</td></tr>
<tr><td><code>multiple</code></td><td>複数選択許可</td></tr>
<tr><td><code>capture</code></td><td>モバイルでカメラ起動 (<code>user</code> / <code>environment</code>)</td></tr>
<tr><td><code>webkitdirectory</code></td><td>ディレクトリ単位選択 (Chromium/Firefox)</td></tr>
</tbody>
</table>

<h2>FileReader で内容を読む</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const reader = new FileReader();

// テキストとして読む
reader.onload = () => console.log(reader.result);
reader.readAsText(file, &apos;UTF-8&apos;);

// データ URL (Base64) として読む → img.src へ
reader.onload = () => document.getElementById(&apos;img&apos;).src = reader.result;
reader.readAsDataURL(file);

// ArrayBuffer として読む (バイナリ操作)
reader.onload = () => {
  const buf = reader.result;
  const view = new Uint8Array(buf);
  console.log(&apos;first 4 bytes:&apos;, view.slice(0, 4));
};
reader.readAsArrayBuffer(file);

// 進捗・エラー
reader.onprogress = (e) => {
  if (e.lengthComputable) console.log((e.loaded / e.total * 100).toFixed(1) + &apos;%&apos;);
};
reader.onerror = () => console.error(reader.error);

// 中断
reader.abort();</code></pre>
<h3>モダンな書き方 (await + File メソッド)</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 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(&apos;chunk&apos;, value.byteLength);
}</code></pre>
<h2>URL.createObjectURL で表示</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const url = URL.createObjectURL(file);
document.getElementById(&apos;preview&apos;).src = url;

// 不要になったら解放 (メモリリーク防止)
img.onload = () => URL.revokeObjectURL(url);</code></pre>
<h2>Drag & Drop</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><div id="drop" style="border:2px dashed #888;padding:40px;text-align:center">
  ここにファイルをドロップ
</div>
<script>
const drop = document.getElementById(&apos;drop&apos;);

drop.addEventListener(&apos;dragover&apos;, (e) => {
  e.preventDefault();             // 必須: drop を有効化
  drop.style.background = &apos;#eef&apos;;
});
drop.addEventListener(&apos;dragleave&apos;, () => drop.style.background = &apos;&apos;);
drop.addEventListener(&apos;drop&apos;, (e) => {
  e.preventDefault();
  drop.style.background = &apos;&apos;;
  const files = e.dataTransfer.files;
  for (const f of files) console.log(f.name);
});
</script></code></pre>
<h2>クリップボード経由</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 貼り付けイベントから画像取得
document.addEventListener(&apos;paste&apos;, async (e) => {
  for (const item of e.clipboardData.items) {
    if (item.type.startsWith(&apos;image/&apos;)) {
      const file = item.getAsFile();
      console.log(&apos;pasted image:&apos;, file.name, file.size);
    }
  }
});

// クリップボードに書き込み (要 user gesture)
const blob = new Blob([&apos;hello&apos;], { type: &apos;text/plain&apos; });
await navigator.clipboard.write([new ClipboardItem({ &apos;text/plain&apos;: blob })]);</code></pre>
<h2>fetch でアップロード</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// FormData (multipart)
const fd = new FormData();
fd.append(&apos;upload&apos;, file, file.name);
fd.append(&apos;title&apos;, &apos;sample&apos;);
const res = await fetch(&apos;/api/upload&apos;, { method: &apos;POST&apos;, body: fd });

// Blob を直接 PUT
await fetch(&apos;/api/upload&apos;, {
  method: &apos;PUT&apos;,
  body: file,
  headers: { &apos;Content-Type&apos;: 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(&apos;/api/upload-chunk?offset=&apos; + i, { method: &apos;POST&apos;, body: chunk });
}</code></pre>
<h2>ダウンロード (Blob 経由)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">function downloadBlob(blob, filename) {
  const a = document.createElement(&apos;a&apos;);
  a.href = URL.createObjectURL(blob);
  a.download = filename;
  a.click();
  URL.revokeObjectURL(a.href);
}

// CSV をその場で生成してダウンロード
const csv = &apos;name,score\nAlice,80\nBob,75&apos;;
downloadBlob(new Blob([csv], { type: &apos;text/csv&apos; }), &apos;scores.csv&apos;);</code></pre>
<h2>File System Access API (Chrome 系)</h2>

<p>従来の File API が読み取り中心なのに対し、<strong>File System Access API</strong> はユーザーの許可下でローカルファイル/ディレクトリへの<strong>書き込み</strong>も可能にします (Chromium 系のみ)。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// ファイルを開いて読み書き
const [handle] = await window.showOpenFilePicker({
  types: [{ description: &apos;Text&apos;, accept: { &apos;text/plain&apos;: [&apos;.txt&apos;] } }],
});
const file = await handle.getFile();
console.log(await file.text());

// 同じ場所に書き戻し
const w = await handle.createWritable();
await w.write(&apos;updated content&apos;);
await w.close();

// ディレクトリ選択
const dir = await window.showDirectoryPicker();
for await (const [name, h] of dir.entries()) {
  console.log(name, h.kind);  // &apos;file&apos; or &apos;directory&apos;
}

// 新規ファイル保存
const saveHandle = await window.showSaveFilePicker({
  suggestedName: &apos;note.txt&apos;,
  types: [{ accept: { &apos;text/plain&apos;: [&apos;.txt&apos;] } }],
});
const ws = await saveHandle.createWritable();
await ws.write(&apos;hello&apos;);
await ws.close();</code></pre>
<h2>IndexedDB との連携</h2>

<p>Blob は IndexedDB に直接格納できるため、オフラインキャッシュやローカル DB と組み合わせやすい:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const db = await openDB(&apos;files&apos;, 1, {
  upgrade(db) { db.createObjectStore(&apos;store&apos;, { keyPath: &apos;name&apos; }); },
});
await db.put(&apos;store&apos;, { name: file.name, blob: file });
const saved = await db.get(&apos;store&apos;, &apos;photo.jpg&apos;);
const url = URL.createObjectURL(saved.blob);</code></pre>
<h2>セキュリティ・制限事項</h2>

<ul>
<li><strong>同一オリジンポリシー</strong>: ドラッグ元クロスオリジンの blob URL は使えないことがある</li>
<li><strong>ユーザー操作起点</strong>: showOpenFilePicker / showSaveFilePicker はクリックなど gesture 内でのみ呼び出し可能</li>
<li><strong>サイズ制限</strong>: ブラウザの IndexedDB / メモリ制限。大容量は Streams API でチャンク処理</li>
<li><strong>ファイル名は安全に</strong>: アップロード後はサーバ側で正規化 (拡張子強制、ハッシュ名)</li>
</ul>

<h2>ブラウザ対応</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>API</th><th>Chrome</th><th>Firefox</th><th>Safari</th></tr></thead>
<tbody>
<tr><td>FileReader / Blob / File</td><td>○</td><td>○</td><td>○</td></tr>
<tr><td>file.text() / arrayBuffer()</td><td>○</td><td>○</td><td>○</td></tr>
<tr><td>Drag & Drop</td><td>○</td><td>○</td><td>○</td></tr>
<tr><td>Clipboard image paste</td><td>○</td><td>○</td><td>○</td></tr>
<tr><td>File System Access</td><td>○</td><td>×</td><td>×</td></tr>
<tr><td>webkitdirectory</td><td>○</td><td>○</td><td>△</td></tr>
</tbody>
</table>

<h2>FAQ</h2>

<p><strong>Q: Base64 (DataURL) と Blob URL、どちらを使うべき?</strong><br />
A: <strong>表示用には Blob URL</strong>。Base64 はメモリ・転送効率が悪い (約 33% 増)。サーバへ JSON 形式で送る必要があるときだけ Base64。</p>

<p><strong>Q: ローカルに自由に書き込めないの?</strong><br />
A: 任意の場所は不可。File System Access API でユーザー許可を得た範囲のみ可能。それ以外は IndexedDB / OPFS (Origin Private File System) を使う。</p>

<p><strong>Q: 巨大ファイル (1GB 超) をアップロードしたい</strong><br />
A: <code>file.slice()</code> でチャンク化し、サーバ側で結合。S3 マルチパートアップロードや tus プロトコルを使うと冪等で堅牢。</p>