[Astro] #127 Archive Manager — libarchive.js & JSZipによるブラウザ完結型アーカイブ展開・文字化け自動修復ツール

[Astro] #127 Archive Manager — libarchive.js & JSZipによるブラウザ完結型アーカイブ展開・文字化け自動修復ツール

概要

ZIP、7z、RAR、tar.gz などの各種アーカイブファイルを、サーバーへアップロードすることなくブラウザ上だけで安全に解凍・閲覧・抽出できるツールを実装しました。

コアとなる解凍処理には libarchive の WebAssembly ビルドである libarchive.js と、生バイトレベルでの制御が可能な JSZip を併用するハイブリッド構成を採用しています。

日本語環境特有の課題である「Windowsで作成されたZIPファイルをMacやWebブラウザで解凍するとファイル名が文字化けする」問題をShift-JIS (CP932) の自動判定・フォールバックによって完全解決しているほか、悪意あるアーカイブを検知するセキュリティチェック機能も備えています。

スクリーンショット

ZIPファイル読み込み

[Astro] #127 Archive Manager — 文字化け修復後のファイルツリー表示

画像ファイルをクリック ➡ 画像表示

[Astro] #127 Archive Manager — 文字化け修復後のファイルツリー表示

JSON/TXTファイルをクリック ➡ テキスト表示

[Astro] #127 Archive Manager — 文字化け修復後のファイルツリー表示

動画(GIF)

[Astro] #127 Archive Manager — アーカイブ読み込みとリアルタイム解凍の挙動

機能一覧

カテゴリ内容
対応形式ZIP, 7z, RAR, tar, tar.gz, tar.bz2, tar.xz など
エンコード修復Shift-JIS (CP932) / UTF-8 の自動判定・手動トグル切替
閲覧・ツリー表示ディレクトリ構造のツリーレンダリング、拡張子別アイコン表示
プレビュー画像(PNG, JPG, WebP等)、テキスト、各種コード、3Dモデル用ファイル(.vmd, .vpd等)の即時オーバーレイ表示
セキュリティ診断ZIP爆弾(圧縮率100倍超)、二重拡張子、パス・トラバーサル、マジックバイト偽装の自動検出
エクスポートファイル単体ダウンロード / 文字化け修復済みのUTF-8一括ZIP再展開
処理方式完全クライアントサイド処理(WebWorker / WASM)

1. アーカイブ解析 — WASM と JSZip のハイブリッド構造

libarchive.js の課題と文字化け問題

最初期の実装では libarchive.js のみで全アーカイブのパースを完結させようとしていました。しかし、Windowsの標準機能で圧縮されたShift-JIS (CP932) のZIPファイルを読み込んだ際、WASMのC/C++内部処理の段階で不正なバイト列が UTF-8 の置換文字 “ (U+FFFD) に不可逆変換されてしまう問題が発生しました。

JavaScript層へ文字が引き渡された時点で元の生バイト(Uint8Array)情報が喪失しているため、後から TextDecoder('shift-jis') を呼び出しても元に戻せなくなっていたのです。

JSZip との併用によるハイブリッド設計

この問題を回避するため、フォーマットに応じたパーサーの動的切り替えシステムを導入しました。

  • ZIPファイル: 生バイトへのアクセスが可能な JSZip を使用し、ヘッダーのバイト配列をそのまま確保
  • 7z / RAR / tar 等: 広範なフォーマットに対応する libarchive.js (WASM) で処理
if (isZip) {
  // ZIPの場合はJSZipでパース(Shift-JISの生バイト列を保持)
  flatFiles = await parseZipWithJSZip(file, forceSJIS);
} else {
  // 7z / tar / rar 等は libarchive.js でパース
  const archive = await Archive.open(file);
  const filesArray = await archive.getFilesArray();
  // ...
}

2. Shift-JIS (CP932) 文字化けの自動判定・修復ロジック

判定の落とし穴と解決策

単に TextDecoder('shift-jis') を実行するだけでは、Linux/Macで作成された通常の UTF-8 ZIP を誤って文字化けさせてしまいます。また、「“ が含まれているか」などの曖昧な文字列検索判定を行うと、Shift-JISの特殊記号や機種依存文字が含まれていた場合に誤判定を起こし、フォールバックに失敗するケースがありました。

最終的に、「厳密な UTF-8 デコードの成否」を例外キャッチで判定する2段階デコード構造を採用しました。

function decodeBytes(bytes, forceSJIS) {
  if (!forceSJIS) {
    return new TextDecoder('utf-8').decode(bytes);
  }

  // 1. まず UTF-8 (厳密モード) で試行
  try {
    return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
  } catch (e) {
    // UTF-8 として不正なバイト列(WindowsのShift-JIS / CP932)の場合、ここへ到達
  }

  // 2. Shift-JIS としてデコード
  try {
    return new TextDecoder('shift-jis').decode(bytes);
  } catch (e) {
    return new TextDecoder('utf-8').decode(bytes);
  }
}

このアプローチにより、以下の挙動を完璧に自動判別できるようになりました。

  1. fatal: true オプション: UTF-8構造として破綻しているバイト順序に遭遇した瞬間に例外を発生させる
  2. CP932の正確なデコード: 例外を検知した場合のみ shift-jis に安全にフォールバックする
  3. 生バイト記憶: パース時に rawBytes を保持しておくことで、オプションのトグルスイッチ切替時にアーカイブ全体の再読み込みをすることなく即座にリアルタイム再描画を行う

3. 静的セキュリティ診断機能 (Security Check)

解凍前にファイル群の構造を走査し、潜在的な脆弱性や悪意あるファイルを検出するセキュリティチェックエンジンを内蔵しています。

チェック項目

  • ZIP爆弾 (ZIP Bomb): 展開後の総サイズとアーカイブ容量の比率を計算(100倍を超えた場合に ⚠️ ZIP BOMB 警告)
  • 実行可能ファイル: .exe, .bat, .vbs, .sh などの実行可能拡張子の検出(🔴 Executable
  • 二重拡張子 (Double Extension): image.png.exe のような偽装パターンの検出(🟡 Double extension
  • パス・トラバーサル: ../ やルート相対パスが含まれる危険な構造の検出(🔴 Path traversal
  • マジックバイト偽装: ファイル先頭の数バイト(マジックナンバー)を読み込み、拡張子と実際のファイル形式の不一致を自動検出
function detectMagicMismatch(name, bytes) {
  const ext = getExtension(name).replace('.', '');
  // 例: 画像拡張子なのに先頭バイトが 'MZ' (0x4D, 0x5A = EXEヘッダー) の場合を警告
  if (bytes.length >= 2 && bytes[0] === 0x4D && bytes[1] === 0x5A) {
    return `Extension is .${ext} but file is actually an executable (MZ header)`;
  }
  // ...
}

4. UIアーキテクチャ & インラインプレビュー

動的ツリーレンダリング

フラットなパス一覧(例: cat/motion/01.vmd)から階層構造オブジェクト(TreeNode)を動的に構築し、フォルダの開閉が可能なツリーUIを生成します。

interface TreeNode {
  name: string;
  path: string;
  isDir: boolean;
  size: number;
  children: TreeNode[];
  file?: any;
}

ブラウザ内安全プレビュー

解凍された Blob データを抽出し、以下のファイルをローカルのメモリ上だけで安全にプレビューできます。

  • 画像ファイル: ObjectURL化して <img /> レンダリング
  • テキスト / 記述コード: UTF-8テキストとして読み込み、エスケープ処理を施して <pre> 表示
  • 3D関連ファイル: MMD等の .vpd.vmd 設定テキストの構造確認

技術スタック

  • JSZip — ZIP形式のパース、文字コードの生バイト制御、一括再ZIP化
  • libarchive.js — 7z / RAR / tar 等多形式アーカイブのWASMパース
  • TextDecoder API — UTF-8 (fatal) および Shift-JIS (CP932) のバイトデコード
  • Astro — コンポーネントビルドおよびクライアントサイドスクリプト組み込み

今後の展望

WASMおよびクライアントサイド処理系ツール第2弾として、展開機能に特化したWebアーカイブマネージャーを構築しました。

今後の拡張として、解凍だけでなくブラウザ内での新規7z/ZIP圧縮機能、暗号化ZIP(パスワード付き)の解凍対応、Web Workerを用いた大容量ファイル解凍時のUIフリーズ完全防止などを検討しています。