1. 記事一覧 >
  2. ブログ記事
Power Automate
category logo

Power AutomateとOffice Scriptsを使ってエクセルから画像取得&別ブックに画像挿入

(更新) (公開)

はじめに

Power Automate と Office Scripts(Office スクリプト)を使ってエクセルから画像取得し、別ブックに画像挿入するというのをやってみました。
全手順を紹介していきます。

エクセルから画像取得し、別ブックに画像挿入

【 Office Script(Office スクリプト) 】

エクセルマクロ VBA の JavaScript(TypeScript ) 版です。

【 Power Automate 】

Power Automate は、よく使うアプリやサービスとの間に自動化されたワークフローを作成し、ファイルの同期、通知の受信、データの収集などを行うことができるようにするためのサービスです。


【仕様/前提】
・取得元ブック、挿入先ブックともに、A 列は、id、B 列は、画像(シェイプ)とします。
・画像の左上の座標が入っているセルを画像が所属するセルと定義します。(例えば、B2 セルの画像がセルからはみ出していても、左上角が B2 セルにある場合、B2 セルの画像とします。)
左上角が B2 セルにある場合、B2 セルの画像 ・画像一つ一つにユニークな id があるものとします。
・id 、画像は必ず両方あるものとします。(どちらか片方の行は無いものとします。)
・B 列に画像が2つ以上所属している場合、どれか一つだけ有効とします。
・挿入後の画像は、アスペクト比固定で挿入先セルにぴったり収まるものとします。

今回エクセルと言っているのは、全て、Microsoft 365 のエクセルの事です。

やっている途中で気付きましたが、今回のケースの場合、Power Automate を使う必要は無く、Office Scripts のみでもっと簡単にできます。

取得元、挿入先どちらかがエクセルではない場合、Power Automate が活用できると思います。

Power Automate 活用例:

 画像取得元が SharePoint → エクセルのセルに画像を貼り付け

 画像取得元がエクセル → Dataverse のテーブルレコードに画像登録


エクセルブック準備

Microsoft 365 の Excel にて、ブックを作成します。

ブックを作成


挿入 → 画像 → このデバイス... にて png 画像ファイルを選択して、セルに挿入します。

画像ファイルを選択して、セルに挿入


画像をセルに挿入後


なるべくセルの中に入れましたが、左上角がかかっていれば、はみ出していても構いません。

左上角が B2 セルにある場合、B2 セルの画像

入力した範囲を選択して、ホーム → テーブルとして書式設定 でテーブル化します。

テーブル化は、Power Automate で扱うための処置で、Office Scripts のみで完結する場合は、必要ありません。

テーブルとして書式設定


挿入先のブックを作成します。


画像のセルが空白であること以外、先ほどと同じ手順で作成します。
ここで、画像列のセルの大きさは、取得元ブックと同じである必要はありません。

挿入先のブックを作成


id 取得フロー作成

Power Automate でフローを作成します。


+作成 → インスタントクラウドフロー → 手動でフローをトリガーします → 作成 とします。

インスタントクラウドフロー作成


手動でフローをトリガーします


表内に存在する行を一覧表示 アクションを作成して、画像取得元ブック、テーブルを指定します。

表内に存在する行を一覧表示アクション


それぞれに適用する(Apply to each) コントロールを作成します。
以前の手順から出力を選択 のところに、value(outputs('表内に存在する行を一覧表示')?['body/value'])を指定します。

以前の手順から出力を選択


これで1行1行の id を取り出せるようになりました。


画像取得スクリプト作成

1行1行の id(items('Apply_to_each')?['id'])を取り出せるようになりましたが、画像(items('Apply_to_each')?['画像']) を指定しても画像は取り出されません。空文字が取り出されます。

画像を指定しても画像は取り出されません。


取り出し元ブックの方に、画像を取り出す Office Scripts を作成します。


自動化 → 新しいスクリプト をクリックします。

新しいスクリプト


スクリプトの内容を以下のようにして、スクリプトを保存します。

画像を取り出す Office Scripts を保存

// 引数は id を受け取る
// 画像の Base64 を返すため、戻り値は、string
function main(workbook: ExcelScript.Workbook, id: string): string {
  let top: number;
  let left: number;
  let width: number;
  let height: number;
  let targetShape: ExcelScript.Shape; //特定するシェイプ
  // sheet = 最初のシート
  const sheet: ExcelScript.Worksheet = workbook.getWorksheets()[0];
  // データが入力されている全体の範囲
  const range: ExcelScript.Range = sheet.getUsedRange();
  // rows = データが入力されている行数
  const rows = range.getRowCount();
  // 1行ずつ処理
  for (let row = 0; row < rows; row++) {
    // A[行数]セルが引数のidと一致するかどうか
    if (range.getCell(row, 0).getText() == id) {
      // 対象行B列のセルの大きさを得る
      top = sheet.getRange(`B${row + 1}`).getTop();
      left = sheet.getRange(`B${row + 1}`).getLeft();
      width = sheet.getRange(`B${row + 1}`).getWidth();
      height = sheet.getRange(`B${row + 1}`).getHeight();
      // getShapes() = 全シェイプ からシェイプを一つずつ取り出し
      for (let shape of sheet.getShapes()) {
        // 左上角の座標を得る
        let shapeTop: number = shape.getTop();
        let shapeLeft: number = shape.getLeft();
        // シェイプが画像かつ、左上角が対象行B列のセルの中にある場合、対象シェイプ
        if (
          shape.getType() === ExcelScript.ShapeType.image &&
          shapeTop >= top &&
          shapeTop <= top + height &&
          shapeLeft >= left &&
          shapeLeft <= left + width
        ) {
          targetShape = shape;
          break; // 対象シェイプが見つかったら、抜ける。
        }
      }
      break; // 対象行が見つかったら、抜ける。
    }
  }
  // 対象シェイプの画像のBase64を返す。
  return targetShape.getAsImage(ExcelScript.PictureFormat.png);
}

画像取得スクリプト実行

Office Scripts を作成したら、Power Automate のフローに戻って、スクリプトの実行 アクションを作成して、先ほどのスクリプトを指定し、id を渡すようにします。

スクリプトの実行アクション

実行結果は、画像の base64 文字列です。iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAA...のような長い文字列です。


行の更新

行の更新 アクションを作成して、挿入先ブック A 列に id をコピーしておくようにします。

行の更新アクションを作成

この処理は必須ではなく、挿入先ブック A 列に最初から id を書いておけば問題ありませんが、念のため、実行しておきます。


画像挿入スクリプト作成

挿入先ブックの方に、画像を挿入する Office Scripts を作成します。


自動化 → 新しいスクリプト をクリックします。


スクリプトの内容を以下のようにして、スクリプトを保存します。

// 引数として、id、base64文字列を受け取る。
function main(workbook: ExcelScript.Workbook, id: string, imageBase64: string) {
  //最初のシートを取得
  const sheet: ExcelScript.Worksheet = workbook.getWorksheets()[0];
  // base64文字列→画像のシェイプに変換
  const image: ExcelScript.Shape = sheet.addImage(imageBase64);
  // データが入力されている全体の範囲
  const range: ExcelScript.Range = sheet.getUsedRange();
  // rows = データが入力されている行数
  const rows = range.getRowCount();
  // 1行ずつ処理
  for (let row = 0; row < rows; row++) {
    // A[行数]セルが引数のidと一致するかどうか
    if (range.getCell(row, 0).getText() == id) {
      //該当行B列のセル
      const cell: ExcelScript.Range = sheet.getRange(`B${row + 1}`);
      //画像シェイプ左上角の座標を該当行B列のセルの左上座標に合わせる
      image.setTop(cell.getTop());
      image.setLeft(cell.getLeft());
      //画像シェイプアスペクト比を固定
      image.setLockAspectRatio(true);
      const height: number = cell.getHeight();
      const width: number = cell.getWidth();
      // セルの横幅が高さより大きい場合
      // (画像がセルからはみ出さないようにセル内にぴったり収める)
      if (width > height) {
        // 画像の高さをセルの高さに合わせる
        // (アスペクト比を固定しているため、幅も自動的に変更される)
        image.setHeight(cell.getHeight());
      } else {
        // セルの横幅が高さより小さい場合
        // 画像の幅をセルの幅に合わせる
        // (アスペクト比を固定しているため、高さも自動的に変更される)
        image.setWidth(cell.getWidth());
      }
      break; // 対象行が見つかったら、抜ける。
    }
  }
}

【 余談 】

最後の画像をセルにフィットさせているところですが、

https://learn.microsoft.com/ja-jp/javascript/api/office-scripts/excelscript/excelscript.placement?view=office-scripts

を参考に

image.setPlacement(ExcelScript.Placement.twoCell);

にて、試みましたが、何も変わりませんでした。

なぜか、参考 URL の例も

// image.setPlacement(ExcelScript.Placement.twoCell);

とコメントアウトしても何も変わりませんでした。


画像取得スクリプト実行

Office Scripts を作成したら、Power Automate のフローに戻って、スクリプトの実行 2 アクションを作成して、先ほどのスクリプトを指定し、id、result を渡すようにします。
ここで、result とは、outputs('スクリプトの実行')?['body/result'] のことで、画像の base64 文字列です。

スクリプトの実行 2アクションを作成


フロー実行

フローを実行します。

フローを実行 挿入先ブック


結構時間がかかります。...というか、めちゃくちゃ遅いです。
とにかく、できたので、ヨシッ!


何回か起動していると、以下のエラーになったことがあります。何も変えずにもう一度実行すると、成功しました。

100% 成功するものではないと思っておいた方が良いかもしれません。

スクリプトを実行できませんでした。もう一度お試しください。

Office JS エラー:Line 7: Range getRowCount: The request failed with status code of 503, error code UnknownError and the following error message:

clientRequestId: 22******-****-****-****-**********18


loading...