ブラウザの機能拡張に伴い、複雑なサードパーティ製ライブラリに依存せず、標準 Web API だけでファイル入出力を実現するケースが増加しています。以下では、ネイティブ JavaScript を用いた代表的なファイル処理フローを整理します。
1. ファイル読取とフォームベース送信
ユーザーがローカルから選択したファイルを FileReader でテキスト化し、FormData でパッケージ化してサーバーへ転送する基本的なパターンです。UI は最小限のスタイリングとし、チェックボックスの状態に応じた入力フィールドの表示切り替えも実装しています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<style>
.upload-trigger { padding: 8px 14px; background: #eef5ff; border: 1px solid #b0c8e8; cursor: pointer; border-radius: 4px; }
.upload-trigger input { position: absolute; opacity: 0; width: 100%; height: 100%; top: 0; left: 0; cursor: pointer; }
.config-panel { margin-top: 12px; padding: 10px; border: 1px dashed #ccc; }
.hidden { display: none; }
</style>
</head>
<body>
<div class="form-group">
<label class="upload-trigger">
参照ボタン
<input type="file" id="sourceInput" accept=".json,.csv,.txt">
</label>
<button onclick="submitProcessedFile()">データ送信</button>
<pre id="outputLog"></pre>
</div>
<div class="config-panel">
<label><input type="checkbox" id="chkOverride" onchange="toggleMetaFields()"> 既存レコードを上書きする</label>
<div id="extraFields" class="hidden" style="margin-top:8px;">
<input type="text" id="targetDbName" placeholder="DB識別名" style="margin-right:8px;">
<input type="text" id="targetLabel" placeholder="表示ラベル">
</div>
</div>
<div style="margin-top:20px;">
<input type="text" id="reqTableId" placeholder="対象テーブルID">
<button onclick="saveMetadataLocally()">メタ情報をダウンロード</button>
</div>
<script>
function toggleMetaFields() {
const isChecked = document.getElementById('chkOverride').checked;
document.getElementById('extraFields').classList.toggle('hidden', isChecked);
}
async function submitProcessedFile() {
const fileEl = document.getElementById('sourceInput');
const targetFile = fileEl.files[0];
if (!targetFile) return alert('ファイルを選択してください');
const reader = new FileReader();
reader.onload = async (evt) => {
try {
const formData = new FormData();
formData.append('payload', evt.target.result);
formData.append('allowReplace', document.getElementById('chkOverride').checked);
formData.append('overrideDb', 'sample_db_v2');
formData.append('overrideLabel', '一括インポート用');
const resp = await fetch('/api/import/config', {
method: 'POST',
body: formData
});
const json = await resp.json();
document.getElementById('outputLog').textContent = json.statusMsg || '処理正常終了';
} catch (err) {
console.error('転送失敗:', err);
document.getElementById('outputLog').textContent = 'ネットワークエラーが発生しました';
}
};
reader.readAsText(targetFile);
}
async function saveMetadataLocally() {
const tableId = document.getElementById('reqTableId').value;
if (!tableId) return alert('テーブルIDを入力してください');
try {
const res = await fetch('/api/export/schema', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tableId })
});
const data = await res.json();
if (data.code === 200) {
const blob = new Blob([data.content], { type: 'application/json;charset=utf-8' });
const tempUrl = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = tempUrl;
anchor.download = `schema_${tableId}.json`;
anchor.click();
URL.revokeObjectURL(tempUrl);
} else {
alert(data.errMsg || 'スキーマ取得に失敗しました');
}
} catch (err) {
console.error('保存失敗:', err);
}
}
</script>
</body>
</html>
2. JSONペイロード送信とバイナリエクスポート
バックエンドへフィルター条件を JSON 形式で送信し、返却されるバイナリデータ(ExcelやCSVなど)をクライアント側でダウンロードする実装です。XHR に responseType: 'blob' を設定し、生成されたオブジェクト URL を一時的に付与したアンカータグをクリックさせることで保存処理を実行します。
/**
* 検索条件を指定してレポートデータを取得・保存
*/
async function exportReport() {
const filterCriteria = collectQueryParams();
const endpoint = '/reports/generate/daily';
const xhr = new XMLHttpRequest();
xhr.open('POST', endpoint, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200 && xhr.response.size > 0) {
const fileBlob = xhr.response;
const objectUrl = URL.createObjectURL(fileBlob);
const dlAnchor = document.createElement('a');
dlAnchor.style.display = 'none';
dlAnchor.href = objectUrl;
dlAnchor.download = 'daily_report.xlsx';
document.body.appendChild(dlAnchor);
dlAnchor.click();
// リソース解放防止のため DOM と URL をクリーンアップ
setTimeout(() => {
document.body.removeChild(dlAnchor);
URL.revokeObjectURL(objectUrl);
}, 100);
} else {
console.warn(`エクスポート応答ステータス: ${xhr.status}`);
}
};
xhr.onerror = () => console.error('通信が切断されました');
xhr.send(JSON.stringify(filterCriteria));
}
function collectQueryParams() {
// フィルター値の収集ロジック
return { status: 'completed', period: 'last_7_days' };
}
3. サーバーサイドのレスポンス制御(Java)
バイナリストリームを HTTP レスポンスとして送信する際、ファイル名の文字化けを防ぐため UTF-8 エンコーディングを適用するのが現代の標準的な実装です。従来ブラウザごとの分岐処理は省略し、規格に準拠したシンプルな記述に統一しています。
private void sendBinaryStream(HttpServletResponse response, String rawFilename, Workbook workbook) throws IOException {
String encodedName = URLEncoder.encode(rawFilename, StandardCharsets.UTF_8.name());
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", String.format("attachment; filename*=UTF-8''%s", encodedName));
try (OutputStream out = response.getOutputStream()) {
workbook.write(out);
out.flush();
} catch (Exception e) {
throw new RuntimeException("ファイルストリームの書き込みに失敗しました", e);
}
}