はじめに
皆様はキャンバスアプリのリッチ テキスト エディターを使うことがありますか?
OneNote
やメールでもおなじみ、文字装飾ができる非常に便利な機能です。
このような機能ですと、スクリーンショット
をはじめとした画像の貼り付け、ということも当然やりたくなります。
いざ使ってみるとリッチ テキスト エディターには 画像の貼り付け自体はできます。
一方でデータは登録されるでしょうか?下記のリストで試してみましょう。
Patch(
Test_RichText, // SharePoint Lists
Defaults(Test_RichText), // 新規登録
{TestHTML: rteImageWriter.HtmlText} // リッチ テキスト エディター
);
この設定の列を作成し、リッチテキストを保存してみます。
画像が無視されて情報が登録されました😲
この挙動はMicrosoft MVP for Business Applicationsのヨウセイさんのブログが非常に参考になります。
Dataverse
であればいい感じにできますが、SharePoint Listsの場合は上手くできません。なんってこった。
SharePoint Listsの列の種類 - 複数行テキスト
公式サイトを見てみましょう。
リストの列内ではテキストの既定の設定は、プレーン テキストです。 ただし、[拡張リッチ テキスト] 列の設定を選択すれば、テキストのフォントの色、サイズ、またはスタイルまたはマークアップをカスタマイズできます。 [拡張リッチ テキスト] 列を作成するには、[編集] アクセス許可レベルまたはそれ以上のレベルが必要です。
この列の種類であれば画像もサポートされているのだな、という想像がわきます。
実際のリストの設定では 拡張リッチ テキストを使用 (画像、表、ハイパーリンクを含むリッチ テキスト)
をオンにすることで、画像を挿入することが可能です。
編集のメニューが複数存在します。
画像はソースを指定することで挿入できるようですね。
👇わたしのプロフィール画像のリンクを設定した例です。
それではPower Apps キャンバスアプリ
でリッチ テキスト エディター
に貼り付けた画像を、このSharePoint Lists
の列に反映するにはどのようにすればいいでしょうか。
リッチ テキスト エディターの値
実際にキャンバス アプリを作成し、リッチ テキスト エディター
に画像と文字を入力してみます。
入力した値は{コントロール名}.HtmlText
で参照することができます。
HTMLを見ていくと<img src="data:image/png;base64,..." />
というようになっています。
base64文字列
で格納されているということですね。
これをそのままSharePoint Lists
にPatch 関数やCollect 関数で保存しようとしても保存されません。
そもそも画像の存在が無視されてしまいます。
SharePoint Lists
の設定を変えて、そのままbase64文字列
として、HTML文字列全体
を複数行テキストで保存することも出来はします。
しかしながら、このサンプルと文字の組み合わせだけでも18,741文字
という大きなデータになってしまい、SharePoint サイト全体に負荷をかけてしまいます。
まったく望ましくない状態です。
しかし 「え~、スクショの挿入できないんですか😒」 といったユーザーの声は聞いたことありませんか?私はたびたびあります。
何とかして実現してみたいと考えたときに天啓がきたので、その内容をご紹介します。
画像付き文字列をSharePoint Listsに保存する
まずbase64文字列
の情報は拡張リッチ テキスト
の保存形式に対してアンマッチになることから、画像の表示をbase64文字列からURL参照形式に変える方法を考えてみましょう。
-
Power Appsのリッチ テキスト エディターに入力された画像 を
SharePoint
・OneDrive
に保存 - 保存した画像のパスを取得する
- リッチ テキスト エディターに入力された文字列を置き換え
上記の流れになります。この中でも(1)
が曲者です。
-
Power Appsのリッチ テキスト エディターに入力された画像 を
SharePoint
・OneDrive
に保存- 文字列の中から、
<img>base64文字列</img>
に該当する部分を抽出 - 1-1で抽出した部分から
dataUri
に該当する部分を取得 -
Power Automate
でファイルとして保存 - 保存した
ファイルのパスを取得
- 文字列の中から、
文字列を解析して中身を抽出する必要があるということですね。
正規表現
とPower Fxを駆使する必要があります。
まず登場する関数はMatchAll 関数です。
ここからは私の知識の範囲で設定した正規表現によるアプローチになります。
より素晴らしいマッチのさせ方も当然ありますので、ご了承ください。
1. HTML文字列から画像を抽出する
まずはリッチ テキスト エディターrteImageWriter
に入力されているHTMLテキストからimgタグ全体
を抽出します。
MatchAll(
rteImageWriter.HtmlText,
"<img[^>]*>"
)
こちらの正規表現<img[^>]*>
は<img
で始まり、>
で終わるimgタグ全体にマッチします。
<img>
タグ全体を抽出します。
しかしこれでは、base64文字列
で設定された画像かどうかに構わずマッチしてしまいます。
正規表現を工夫し、内容を絞る方法が考えられますが、Power Fx
の強力な力を借り、もっと簡単にやってみます。
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/
」の開始位置 -
EndPos
はdataUri
の終了位置引用符"
を検索しています
Excelおなじみのアプローチで部分文字列を抽出する流れです。
この方法でdataUri
を抽出することができたため、リッチ テキスト エディターに直接貼り付けた画像をPower Automateでファイルとして作成する準備が整います。
2. 画像をSharePointに保存する
dataUri
からSharePoint
に画像を保存する方法は単純です。
Power Automate
側はdataUri
を文字列として受け取り、ファイルの作成
アクションでdataUriToBinary関数を使います。
- ファイル名
@{guid()}.png
- ファイル コンテンツ
@{dataUriToBinary(outputs('作成'))}
ファイル名が重複するのを避けるためにguid 関数で特定のライブラリにキャプチャは保存していきましょう。
今回はスクリーンショットの保存をまずは想定しているため、PNG形式で拡張子は決め打ちしています。ご容赦ください
ファイルが保存できたらactions 関数とファイルの作成
の結果を組み合わせて、参照先を返します。
- Path
@{actions('ファイルの作成')?['inputs']?['parameters']?['dataset']}@{outputs('ファイルの作成')?['body/Path']}
Power Automate
は単純な4ステップです。
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 関数で実施します。
// 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が多用しているため真似しています。
改めてみるとコレクションも統合できそうなので、状況に合わせてアレンジしてみてください。
実際の挙動
実際に画面で挙動を見てみましょう。
スクショで貼り付けた画像が、SharePoint
のドキュメント ライブラリに保存され、SharePoint Lists
の拡張リッチテキスト
の列に保存されました。
■ ドキュメント ライブラリ
意外といけるものだなと関心しています。
しかしながらDataverse
使える環境であればこのような手間もないため、ツールに頼れる場合はツールに頼りましょう。