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?

ExcelJSでxlsxファイルから画像を読み込む

Last updated at Posted at 2024-09-28

はじめに

初投稿です。
Reactを使用したアプリ開発で、次のような .xlsx ファイルに挿入された画像を、全件取得してデータベースに保存する機能を実装しました。(xlsxはスプレッドシートで作成)
スクリーンショット 2024-09-29 060017.png
思いのほか難しく、少し手間取ったのでそのメモを投稿します。

目次

用いる技術

  • exceljs: 4.4.0
  • Node.js: 20.12.2
  • TypeScript: 4.9.5
  • @types/node: 16.18.108
  • React: 18.3.1

セットアップ

node.jsをインストール後

reactアプリを作成

npx create-react-app "アプリ名"

最後に、ExcelJsライブラリをインストールします

npm install exceljs

ExcelJS GitHub リポジトリ

.xlsxファイルのアップロード

.xlsxファイルを入力するためのフォームを作成します。

FileInput.tsx
import React, { useState } from 'react';

export const FileInput = () => {
  const [selectedFile, setSelectedFile] = useState<File | null>(null);

  // ファイルが選択されたときに呼ばれる関数
  const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (file && file.name.endsWith('.xlsx')) {
      setSelectedFile(file);
    } else {
      console.log('xlsxファイルを選択してください');
    }
  };
  // ボタンを押下したときに呼ばれる関数
  const handleFileUpload = () => {
    if (!selectedFile) {
      console.log('ファイルが選択されていません');
      return;
    }
    // 画像を読み込む処理を記述
  };

  return (
    <div>
      <input 
        type="file" 
        id="fileInput" 
        name="fileInput" 
        accept=".xlsx" 
        onChange={handleFileChange} />
      <button onClick={handleFileUpload}>選択したファイルから画像を読み込み</button>
    </div>
  );
};

ExcelJsによる画像読み込み

次に、.xlsxファイルを読み込み画像を取得する関数を作成します。

必要なモジュールをインポートし関数を作成

getImages.ts
import ExcelJS from "exceljs";
import { useState } from "react";

export const getImages = async(file:File) => {
    
}

.xlsxファイルをarrayBuffer形式に変換後、シートのデータを読み込む。

getImages.ts
const exelData = await file.arrayBuffer(); // Arrray Buffer(jsでバイナリデータを扱う為の形式)にxlsxを変換
const workbookImg = new ExcelJS.Workbook(); // 画像データを読み込むためにexel jsライブラリでworkbookを作成
await workbookImg.xlsx.load(exelData); //  xlsxデータを作成したworkBookにロード
let imgSheet = workbookImg.worksheets[0]; // シートの読み込み

シートから画像を全件取得し、取得した画像の数だけブックから画像を取得

getImages.ts
const images = imgSheet.getImages(); // シートから画像を全件取得
    let result = []; // 読み込んだ画像データを格納する配列
    for (let i = 0; i < images.length; i++){
        let imageData = workbookImg.getImage(i); // getimage(引数はimageId)を用いて画像データを取得
        result.push(imageData);
    }
}

この方法で画像は正常に取得できましたが、getImage() の引数である imageId は、どうやら .xlsx ファイルに画像が登録された順序に基づいて付与されているようです。
そのため、.xlsx ファイルの1行目にある画像の imageId が 1、2行目の画像の imageId が 2 になると期待していても、実際には1行目の画像の imageId が 10 になってしまう場合もあります。
つまり、.xlsxファイルの画像の順番と、読み込んだ画像の順番が一致しない可能性があります。

よって、.xlsxのセルの位置情報を基に読み込む画像を指定していきます。

getImages.ts
let result = [];
for (let i = 0; i < images.length; i++){
    const targetCell = 'A' + String(i + 2); // セルの指定(A2など)
    const cell = imgSheet.getCell(targetCell); // セルの情報を取得
    const rowIndex = Number(cell.row); // セルの行位置
    const colIndex = Number(cell.col); // セルの列位置
    // 取得したい画像を含むセルの位置情報でimagesをフィルタリングし、特定のセルに含まれる画像を全件取得
    const cellImages = images.filter(image => {
        const { range } = image; // 画像の範囲情報を取得
        return range.tl.col === colIndex - 1 && range.tl.row === rowIndex - 1; // 条件に一致する画像のみを返す
    });

    let imageData = workbookImg.getImage(Number(cellImages[0].imageId)); // フィルタリング画像の先頭のみを取得
    result.push(imageData); // 配列に加える
}

これで、A列の画像を上から順に全件取得できました。
image.png

最後に、ここまでで取得した画像はUint8Array形式 等ですので、file形式に変換するコードを加えて完成です。

getImages.ts
if (imageData && imageData.buffer) {
    const blob = new Blob([imageData.buffer], { type: 'image/png'}); // Blobに変換
    const file = new File([blob], `preItemFile`, { type: 'image/png'}); // Fileに変換
    result.push(file);
}

コードまとめ

.xlsxファイルの入力フォーム

FileInput.tsx
import React, { useState } from 'react';
import { getImages } from './getImages';

export const FileInput = () => {
  const [selectedFile, setSelectedFile] = useState<File | null>(null);

  // ファイルが選択されたときに呼ばれる関数
  const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (file && file.name.endsWith('.xlsx')) {
      setSelectedFile(file);
    } else {
      console.log('xlsxファイルを選択してください');
    }
  };
  // ボタンを押下したときに呼ばれる関数
  const handleFileUpload = () => {
    if (!selectedFile) {
      console.log('ファイルが選択されていません');
      return;
    }
    getImages(selectedFile); // 作成した関数を呼び出し
  };

  return (
    <div>
      <input 
        type="file" 
        id="fileInput" 
        name="fileInput" 
        accept=".xlsx" 
        onChange={handleFileChange} />
      <button onClick={handleFileUpload}>選択したファイルから画像を読み込み</button>
    </div>
  );
};

.xlsxファイルから画像を取得する関数

getImages.ts
import ExcelJS from "exceljs";

export const getImages = async(file:File) => {
    const exelData = await file.arrayBuffer(); // Arrray Buffer(jsでバイナリデータを扱う為の形式)にxlsxを変換
    const workbookImg = new ExcelJS.Workbook(); // 画像データを読み込むためにexel jsライブラリでworkbookを作成
    await workbookImg.xlsx.load(exelData); //  xlsxデータを作成したworkBookにロード
    const imgSheet = workbookImg.worksheets[0]; // シートの読み込み
    const images = imgSheet.getImages(); // シートから画像を全件取得
    let result = [];
    for (let i = 0; i < images.length; i++){
        const targetCell = 'A' + String(i + 2); // セルの指定(A2など)
        const cell = imgSheet.getCell(targetCell); // セルの情報を取得
        const rowIndex = Number(cell.row); // セルの行位置
        const colIndex = Number(cell.col); // セルの列位置
        // 取得したい画像を含むセルの位置情報でimagesをフィルタリングし、特定のセルに含まれる画像を全件取得
        const cellImages = images.filter(image => {
            const { range } = image; // 画像の範囲情報を取得
            return range.tl.col === colIndex - 1 && range.tl.row === rowIndex - 1; // 条件に一致する画像のみを返す
        });
        let imageData = workbookImg.getImage(Number(cellImages[0].imageId)); // フィルタリング画像先頭のみを取得
        if (imageData && imageData.buffer) {
            const blob = new Blob([imageData.buffer], { type: 'image/png'}); // Blobに変換
            const file = new File([blob], `preItemFile`, { type: 'image/png'}); // Fileに変換
            result.push(file);
        }
        result.push(imageData); // 配列に加える
    }
    console.log(result);
}

最後に

本記事では、ExcelJsを用いて.xlsxファイルから画像ファイルを読み込みました。
初心者エンジニアですのでいたらない点が多いかと思いますがご容赦いただけますと幸いです。

参考

ExcelJS GitHub リポジトリ

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?