0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React × JavaScript:ブラウザでWord文書からテキストを抽出する

0
Posted at

日常のフロントエンド開発において、Word文書からのテキスト抽出は高い頻度で求められるニーズです。オンライン文書プレビュー、コンテンツ検索、書式検証、データベースへの取り込みなど、多くのシーンでこの機能が活用されます。従来のアプローチでは、ファイルをバックエンドサーバーにアップロードして処理する必要があり、帯域幅の消費やサーバー負荷が増えるだけでなく、データプライバシーの面でもリスクが伴います。

Spire.Doc for JavaScript は、まったく新しい解決策を提供します。ブラウザ上で直接 WebAssembly を利用して Word 文書の読み込みとテキスト抽出を完了し、バックエンドサービスを一切必要としません。

⚛️ 本記事では、React 技術スタックと組み合わせて、Spire.Doc for JavaScript を用いた Word 文書テキストの抽出とダウンロードの実装方法を詳しく紹介します。

1. 中核技術の原理

1.1 WASM 動作メカニズム

Spire.Doc for JavaScript は、高性能なネイティブ文書処理カーネルを WebAssembly にコンパイルし、ブラウザのサンドボックス内で実行する仕組みです。読み込みプロセスは次の 3 ステップに分かれます。

  • JS グルーコードの読み込みspire.doc.js が WASM モジュールの初期化と API 公開を担当
  • WASM バイナリのコンパイル:ブラウザが .wasm ファイルをダウンロードし、ネイティブマシンコードにコンパイル
  • ランタイムの初期化:仮想ファイルシステム(VFS)を構築し、完全な実行環境を準備

1.2 仮想ファイルシステム(VFS)

WASM はサンドボックス内で動作するため、ローカルファイルシステムに直接アクセスできません。Spire.Doc は Emscripten が提供する FS インターフェースを介して、仮想ファイルシステムをエミュレートします。

  • FS.writeFile を呼び出して、ユーザーがアップロードしたファイルを VFS に書き込む
  • FS.unlink を呼び出して、一時ファイルをクリーンアップする
  • フォントファイルも VFS にプリロードする必要がある

1.3 テキスト抽出の中核 API

Document.GetText() は、プレーンテキストを抽出するための中心的なメソッドです。文書内のすべてのセクション、段落、テキストボックスを走査し、編集可能なテキストコンテンツを順番に連結し、段落構造を維持するために改行文字を保持します。

2. 環境設定とプロジェクト初期化

2.1 依存関係のインストール

まず、React プロジェクトに複数のコンポーネントを統合した Spire.Office をインストールします。

npm i spire.office

この製品には無償のコミュニティ版が提供されています(利用時の制限にご注意ください)。

2.2 コアランタイムファイルの移行

インストール完了後、node_modules 内の Spire.Doc for JavaScript ランタイムファイルを React プロジェクトの public フォルダーにコピーします。

  • spire.doc.js
  • spire.doc.wasm
  • spire.common.js
  • spire.common.wasm
  • _framework フォルダー

📌 なぜ public ディレクトリに配置するのか?
WASM の読み込みは、JavaScript メインスレッドから非同期で .wasm ファイルおよび関連リソースをリクエストする必要があるためです。public ディレクトリに配置することで、ビルドツール(Webpack など)がこれらのファイルを誤って処理したりリネームしたりすることを防ぎ、process.env.PUBLIC_URL 経由で正確にアクセスできるようになります。

2.3 フォントリソースの準備

ブラウザと WASM 環境にはデフォルトでシステムフォントが含まれていません。処理対象の Word 文書で特殊なフォント(Calibri、Times New Roman、中文字体など)が使用されており、VFS にそのフォントが存在しない場合、抽出されたテキストが文字化けしたり、レイアウトが崩れたりする可能性があります。

プロジェクトでよく使用するフォントファイル(例:Arial.ttfCALIBRI.ttfMSYH.TTC など)を public/font/ ディレクトリにまとめて配置することをお勧めします。

3. Word テキスト抽出 - React 完全実装例

以下では、React 関数コンポーネントを用いて、「ファイルアップロード → 解析・抽出 → エクスポート・ダウンロード」の全フローを実装します。

3.1 WASM モジュールの読み込み

import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);
  const [selectedFile, setSelectedFile] = useState(null);
  const [isExtracting, setIsExtracting] = useState(false);
  const [error, setError] = useState(null);
  const [fontLoaded, setFontLoaded] = useState(false);

  // Spire.Doc WASM モジュールを読み込む
  useEffect(() => {
    (async () => {
      try {
        const publicUrl = process.env.PUBLIC_URL || '';
        const spireModule = await import(
          /* webpackIgnore: true */ `${publicUrl}/spire.doc.js`
        );
        const rawModule = spireModule.default || spireModule;
        window.wasmModule = typeof rawModule === 'function'
          ? await rawModule({
              locateFile: (p) =>
                p.endsWith('.wasm') ? `${publicUrl}/${p}` : p,
            })
          : rawModule;
        setWasmModule(window.wasmModule);
      } catch (err) {
        console.error('WASM 読み込み失敗:', err);
        setError('文書処理エンジンを読み込めませんでした。ページを更新して再試行してください。');
      }
    })();
  }, []);

💡 ここでのポイントは、動的 import() を使用して spire.doc.js を読み込み、locateFile コールバックで WASM ファイルのパスを指定することです。/* webpackIgnore: true */ コメントにより、Webpack がこの動的インポートモジュールをバンドル対象にしないようにしています。

3.2 ファイル選択と状態管理

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      setSelectedFile(file);
      setError(null);
    }
  };

ファイル選択ロジックはシンプルで、ユーザーがファイルを選択すると状態に格納し、以前のエラーメッセージをクリアします。

3.3 テキスト抽出コア関数

  const extractTextFromUploadedFile = async () => {
    if (!selectedFile) {
      setError('先に Word 文書を選択してください。');
      return;
    }

    const wasmDoc = window.wasmModule?.spiredoc;
    if (!wasmDoc) {
      setError('文書エンジンがまだ読み込まれていません。しばらくお待ちください。');
      return;
    }

    setIsExtracting(true);
    setError(null);

    try {
      // フォントのプリロード(初回のみ)
      if (!fontLoaded) {
        await window.spire.FetchFileToVFS(
          'Arial.ttf',
          '/Library/Fonts/',
          `${process.env.PUBLIC_URL}/static/font/`
        );
        setFontLoaded(true);
      }

      // アップロードされたファイルを Uint8Array として読み込む
      const arrayBuffer = await selectedFile.arrayBuffer();
      const uint8Array = new Uint8Array(arrayBuffer);
      const fileName = selectedFile.name;

      // VFS に書き込む
      window.dotnetRuntime.Module.FS.writeFile(fileName, uint8Array);

      // Document インスタンスを作成しファイルを読み込む
      const doc = new wasmDoc.Document();
      doc.LoadFromFile(fileName);

      // 全テキストを抽出
      const documentText = doc.GetText();

      // リソースを解放
      doc.Dispose();

      // テキストファイルをダウンロード
      const blob = new Blob([documentText], { type: 'text/plain;charset=utf-8' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `extracted_${selectedFile.name.replace(/\.[^.]+$/, '')}.txt`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);

      // VFS 内の一時ファイルをクリーンアップ
      window.dotnetRuntime.Module.FS.unlink(fileName);

      // 状態をリセット
      setSelectedFile(null);
      document.getElementById('fileInput').value = '';
    } catch (err) {
      console.error('抽出失敗:', err);
      setError(`抽出に失敗しました:${err.message || '不明なエラー'}`);
    } finally {
      setIsExtracting(false);
    }
  };

🛠️ コード解説

  • フォントのプリロードFetchFileToVFS メソッドで public/static/font/ ディレクトリ内の Arial.ttf を WASM の仮想ファイルシステムに読み込み、/Library/Fonts/ パスにマッピングします。
  • VFS へのファイル書き込みFile.arrayBuffer() でファイルのバイナリデータを取得し、Uint8Array を構築してから FS.writeFile で WASM の仮想ファイルシステムに書き込みます。
  • Document 作成と読み込みDocument オブジェクトをインスタンス化し、LoadFromFile メソッドで VFS から Word 文書を読み込みます。
  • テキスト抽出Document.GetText() メソッドを呼び出すだけで、文書内の全テキストを取得できます。
  • リソース解放Dispose() メソッドを呼び出して、Document オブジェクトが占有する WASM メモリリソースを解放し、メモリリークを防止します。
  • ダウンロードとクリーンアップ:抽出されたテキストを Blob オブジェクトにカプセル化し、ダウンロードリンクを作成してブラウザのダウンロードをトリガーします。ダウンロード完了後、作成した一時 URL オブジェクトを解放し、VFS から一時ファイルを削除します。

3.4 UI レンダリング

  return (
    <div style={{ maxWidth: '600px', margin: '50px auto', textAlign: 'center' }}>
      <h1>📄 Word からテキストを抽出</h1>
      <p style={{ color: '#666' }}>ローカルの .doc または .docx ファイルを選択し、全テキストコンテンツを抽出します</p>

      <div style={{ margin: '30px 0' }}>
        <input
          id="fileInput"
          type="file"
          accept=".doc,.docx"
          onChange={handleFileChange}
          disabled={!wasmModule || isExtracting}
          style={{ display: 'none' }}
        />
        <label
          htmlFor="fileInput"
          style={{
            display: 'inline-block',
            padding: '10px 20px',
            background: '#f0f0f0',
            borderRadius: '4px',
            cursor: 'pointer',
            border: '1px solid #ccc',
          }}
        >
          {selectedFile ? `選択中:${selectedFile.name}` : 'Word 文書を選択'}
        </label>
      </div>

      {selectedFile && (
        <button
          onClick={extractTextFromUploadedFile}
          disabled={!wasmModule || isExtracting}
          style={{
            padding: '12px 30px',
            fontSize: '16px',
            backgroundColor: '#007bff',
            color: '#fff',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
            marginTop: '10px',
          }}
        >
          {isExtracting ? '抽出中...' : '抽出してテキストをダウンロード'}
        </button>
      )}

      {error && (
        <div style={{ marginTop: '20px', color: '#d32f2f', background: '#ffebee', padding: '10px', borderRadius: '4px' }}>
          {error}
        </div>
      )}

      {!wasmModule && !error && (
        <div style={{ marginTop: '20px', color: '#888' }}>⏳ 文書エンジンを読み込み中、しばらくお待ちください...</div>
      )}
    </div>
  );
}

export default App;

UI 部分では、ファイル選択、読み込み状態、エラーメッセージ、エンジン読み込み進捗など、ユーザーに対する完全なインタラクションフィードバックを提供します。

3.5 ▶️ 開発サーバーの起動

上記の完全な App コンポーネントコードを src/App.js ファイルにコピーして保存します。プロジェクトルートで次のコマンドを実行します。

npm start

このコマンドで React 開発サーバーが起動し、デフォルトで http://localhost:3000 でアプリケーションが開きます。

  • 「Word 文書を選択」ボタンをクリックして、ローカルの .doc または .docx ファイルをアップロードします。
    ReactWordText.png

  • 「抽出してテキストをダウンロード」ボタンをクリックすると、しばらくして抽出された .txt ファイルが自動的にダウンロードされます。
    ReactExtractText2.png

4. 応用テクニック - 特定の段落やセクションのテキストを抽出する

文書全体のテキストを抽出する必要がない場合は、より細かい API を使用できます。

const doc = new wasmDoc.Document();
doc.LoadFromFile(fileName);

// 最初のセクションを取得
const section = doc.Sections.get_Item(0);
// そのセクションの最初の段落を取得
const paragraph = section.Paragraphs.get_Item(0);
// その段落のテキストを抽出
const text = paragraph.Text;

5. まとめ

以上の例を通じて、ブラウザ側で Word 文書処理機能を実装し、フロントエンドでのテキスト抽出がバックエンドサービスに依存しないようにすることができました。本記事では、React 環境での実装計画を詳細に分解し、WASM 初期化、仮想ファイルシステム操作、リソース解放、例外処理まで、本番環境で必要となるあらゆる詳細を網羅しています。

Spire.Doc for JavaScript がサポートする Word 文書形式は、Word 97-2003 から Word 2019 までの全バージョンをカバーし、DOC/DOCX ファイルを TXT、HTML、PDF、画像など多様な形式に変換することも可能です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?