1
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?

Power Appsのキャンバス アプリでリッチ テキスト エディターに貼り付けた画像を保存する方法

Last updated at Posted at 2025-03-29

はじめに

皆様はキャンバスアプリのリッチ テキスト エディターを使うことがありますか?

OneNoteやメールでもおなじみ、文字装飾ができる非常に便利な機能です。
このような機能ですと、スクリーンショットをはじめとした画像の貼り付け、ということも当然やりたくなります。

image.png

いざ使ってみるとリッチ テキスト エディターには 画像の貼り付け自体はできます。

一方でデータは登録されるでしょうか?下記のリストで試してみましょう。

■ TestHTML 列
image.png

■ 登録するSharePoint Lists
image.png

OnSelect
Patch(
    Test_RichText, // SharePoint Lists
    Defaults(Test_RichText), // 新規登録
    {TestHTML: rteImageWriter.HtmlText} // リッチ テキスト エディター
);

この設定の列を作成し、リッチテキストを保存してみます。

image.png

画像が無視されて情報が登録されました😲

この挙動はMicrosoft MVP for Business Applicationsのヨウセイさんのブログが非常に参考になります。

Dataverseであればいい感じにできますが、SharePoint Listsの場合は上手くできません。なんってこった。

SharePoint Listsの列の種類 - 複数行テキスト

公式サイトを見てみましょう。

リストの列内ではテキストの既定の設定は、プレーン テキストです。 ただし、[拡張リッチ テキスト] 列の設定を選択すれば、テキストのフォントの色、サイズ、またはスタイルまたはマークアップをカスタマイズできます。 [拡張リッチ テキスト] 列を作成するには、[編集] アクセス許可レベルまたはそれ以上のレベルが必要です。

リッチ テキスト リスト列を編集する

この列の種類であれば画像もサポートされているのだな、という想像がわきます。
実際のリストの設定では 拡張リッチ テキストを使用 (画像、表、ハイパーリンクを含むリッチ テキスト)をオンにすることで、画像を挿入することが可能です。

image.png

編集のメニューが複数存在します。

image.png

画像はソースを指定することで挿入できるようですね。

image.png

👇わたしのプロフィール画像のリンクを設定した例です。

image.png

それではPower Apps キャンバスアプリリッチ テキスト エディターに貼り付けた画像を、このSharePoint Listsの列に反映するにはどのようにすればいいでしょうか。

リッチ テキスト エディターの値

実際にキャンバス アプリを作成し、リッチ テキスト エディターに画像と文字を入力してみます。
入力した値は{コントロール名}.HtmlTextで参照することができます。

HTMLを見ていくと<img src="data:image/png;base64,..." />というようになっています。
base64文字列で格納されているということですね。

image.png

これをそのままSharePoint ListsPatch 関数Collect 関数で保存しようとしても保存されません。

そもそも画像の存在が無視されてしまいます。

SharePoint Listsの設定を変えて、そのままbase64文字列として、HTML文字列全体を複数行テキストで保存することも出来はします。
しかしながら、このサンプルと文字の組み合わせだけでも18,741文字という大きなデータになってしまい、SharePoint サイト全体に負荷をかけてしまいます。

まったく望ましくない状態です。

しかし 「え~、スクショの挿入できないんですか😒」 といったユーザーの声は聞いたことありませんか?私はたびたびあります。

何とかして実現してみたいと考えたときに天啓がきたので、その内容をご紹介します。

画像付き文字列をSharePoint Listsに保存する

まずbase64文字列の情報は拡張リッチ テキストの保存形式に対してアンマッチになることから、画像の表示をbase64文字列からURL参照形式に変える方法を考えてみましょう。

  1. Power Appsのリッチ テキスト エディターに入力された画像SharePointOneDriveに保存
  2. 保存した画像のパスを取得する
  3. リッチ テキスト エディターに入力された文字列を置き換え

上記の流れになります。この中でも(1)が曲者です。

  1. Power Appsのリッチ テキスト エディターに入力された画像SharePointOneDriveに保存
    1. 文字列の中から、<img>base64文字列</img>に該当する部分を抽出
    2. 1-1で抽出した部分からdataUriに該当する部分を取得
    3. Power Automateでファイルとして保存
    4. 保存したファイルのパスを取得

文字列を解析して中身を抽出する必要があるということですね。
正規表現Power Fxを駆使する必要があります。

まず登場する関数はMatchAll 関数です。

ここからは私の知識の範囲で設定した正規表現によるアプローチになります。
より素晴らしいマッチのさせ方も当然ありますので、ご了承ください。

1. HTML文字列から画像を抽出する

まずはリッチ テキスト エディターrteImageWriterに入力されているHTMLテキストからimgタグ全体を抽出します。

imgタグをすべて抽出
MatchAll(
    rteImageWriter.HtmlText,
    "<img[^>]*>"
)

こちらの正規表現<img[^>]*><imgで始まり、> で終わるimgタグ全体にマッチします。
<img>タグ全体を抽出します。

しかしこれでは、base64文字列で設定された画像かどうかに構わずマッチしてしまいます。
正規表現を工夫し、内容を絞る方法が考えられますが、Power Fxの強力な力を借り、もっと簡単にやってみます。

imgタグのなかでdata:image/を含むものだけ抽出
Filter(
    MatchAll(
        rteImageWriter.HtmlText,
        "<img[^>]*>"
    ),
    IsMatch(
        FullMatch,
        "data:image/",
        MatchOptions.Contains
    )
)

抽出した<img>タグのうち、data:image/という文字を含むものだけ抽出します。

このアプローチの利点は、リッチ テキスト エディターに直接貼り付けた画像の対して、data:image/は必ず含まれることから、シンプルな抽出が実現できることです。

そしてdataUriの部分を抽出します。

With(
    {
        StartPos: Find(
            "data:image/",
            FullMatch
        ),
        EndPos: Find(
            Char(34),
            FullMatch,
            Find(
                "data:image/",
                FullMatch
            )
        )
    },
    Mid(
        FullMatch,
        StartPos,
        If(
            EndPos > 0,
            EndPos - StartPos,
            Len(FullMatch) - StartPos
        )
    )
)
  • StartPosは 「data:image/」の開始位置
  • EndPosdataUriの終了位置引用符"を検索しています

Excelおなじみのアプローチで部分文字列を抽出する流れです。

この方法でdataUriを抽出することができたため、リッチ テキスト エディターに直接貼り付けた画像をPower Automateでファイルとして作成する準備が整います。

2. 画像をSharePointに保存する

dataUriからSharePointに画像を保存する方法は単純です。

Power Automate側はdataUriを文字列として受け取り、ファイルの作成アクションでdataUriToBinary関数を使います。

image.png

  • ファイル名 @{guid()}.png
  • ファイル コンテンツ @{dataUriToBinary(outputs('作成'))}

ファイル名が重複するのを避けるためにguid 関数で特定のライブラリにキャプチャは保存していきましょう。

今回はスクリーンショットの保存をまずは想定しているため、PNG形式で拡張子は決め打ちしています。ご容赦ください

ファイルが保存できたらactions 関数ファイルの作成の結果を組み合わせて、参照先を返します。

image.png

  • Path @{actions('ファイルの作成')?['inputs']?['parameters']?['dataset']}@{outputs('ファイルの作成')?['body/Path']}

Power Automateは単純な4ステップです。

image.png

3. リッチテキストの値をbase64文字列からパス参照に置き換える

さて仕上げの領域になります。
手法はわかったため、今度は<img>タグの中身を置き換えて保存します。

まずはPower Appsの関数の全体像です。

最初にbase64形式の<img>タグとインデックスのペアcolBase64Data、Power AutomateのもととなるdataUriとインデックスのペアcolDataURIのコレクションを用意します。

コレクションの準備部分
// 1. 初期化 - base64形式とURI別にコレクションを保存
With(
    {
        MatchData: Filter(
            MatchAll(
                rteImageWriter.HtmlText,
                "<img[^>]*>"
            ),
            IsMatch(
                FullMatch,
                "data:image/",
                MatchOptions.Contains
            )
        )
    },
    Concurrent(
        Clear(colBase64Data);
        // 置き換え前文字列のコレクション
        ForAll(
            MatchData,
            Collect(
                colBase64Data,
                {
                    Index: (CountRows(colBase64Data) + 1),
                    FullMatch: FullMatch
                }
            )
        );
        ,
        Clear(colDataURI);
        // Power Automateで渡すDataURI
        ForAll(
            AddColumns(
                MatchData,
                URI,
                With(
                    {
                        StartPos: Find(
                            "data:image/",
                            FullMatch
                        ),
                        EndPos: Find(
                            Char(34),
                            FullMatch,
                            Find(
                                "data:image/",
                                FullMatch
                            )
                        )
                    },
                    Mid(
                        FullMatch,
                        StartPos,
                        If(
                            EndPos > 0,
                            EndPos - StartPos,
                            Len(FullMatch) - StartPos
                        )
                    )
                )
            ),
            Collect(
                colDataURI,
                {
                    Index: (CountRows(colDataURI) + 1),
                    URI: URI
                }
            )
        )
    )
);

上記👆のうち、colDataURIを使ってPower Automateを実施します。

ForAll 関数を使って繰り返し、文字列の置き換え処理を実施するため、単一の文字列をcolReplaceに格納します。

UpdateContext 関数Set 関数では実現できませんが、コレクションであれば繰り返し更新処理を実施することができます。

更新はUpdate 関数で実施します。

Power Automateで画像を保存し、imgの参照を書き換える
// 1. 上書き保存するための一時的なコレクション
/*
 * colReplace - リッチ テキスト エディタに記載されている現在のHTML文字列
 * colResult - Power Automateの結果を保存
 */
Concurrent(
    ClearCollect(
        colReplace,
        {TempText: rteImageWriter.HtmlText}
    );
    ,
    Clear(colResult);
    
);
// 2. Power Automateをつかってスクリーンショットを`SharePoint`に保存、ファイルのフルパスを取得して置き換え文字列を取得
ForAll(
    colDataURI As A,
    Collect(
        colResult,
        {
            Index: A.Index,
            FullPath: dataURItoImageSave.Run(
                Substitute(
                    A.URI,
                    """",
                    ""
                )
            ).path
        }
    );
    Update(
        colReplace,
        First(colReplace),
        {
            TempText: Substitute(
                First(colReplace).TempText,
                LookUp(
                    colBase64Data,
                    Index = A.Index
                ).FullMatch,
                $"<img src='{LookUp(
                    colResult,
                    Index = A.Index
                ).FullPath}' />"
            )
        }
    );
    
);

// 3. SharePoint Listsのデータソースに保存
Patch(
    TestRich,
    First(TestRich),
    {HTMLTest: First(colReplace).TempText}
);

// 4. Clean
Concurrent(
    Reset(rteImageWriter),
    Clear(colBase64Data),
    Clear(colDataURI),
    Clear(colReplace),
    Clear(colResult),
    Notify(
        "Update Complete",
        NotificationType.Information,
        3000
    )
);

Concurrent 関数は処理の並列実行を実現します。
好きな海外のMVPが多用しているため真似しています。

改めてみるとコレクションも統合できそうなので、状況に合わせてアレンジしてみてください。

実際の挙動

実際に画面で挙動を見てみましょう。

image.png

スクショで貼り付けた画像が、SharePointのドキュメント ライブラリに保存され、SharePoint Lists拡張リッチテキストの列に保存されました。

image.png

■ ドキュメント ライブラリ

image.png

意外といけるものだなと関心しています。
しかしながらDataverse使える環境であればこのような手間もないため、ツールに頼れる場合はツールに頼りましょう。

1
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
1
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?