エディタの画像形式を増やす(WebP・BMP・SVG・AVIF)
リッチテキストエディタに貼り付け・アップロードできる画像は JPEG・PNG・GIF の 3 形式だけで、Markdown エディタ(JPEG・PNG・GIF・WebP・BMP)より狭くなっています。この差をなくし、さらに SVG・AVIF を扱う場合に、どこを改修すればよいかの設計メモです。本体の標準機能ではありません。 調査は 1.5.1.0 で行い、前提にした部分を 1.5.8.1 のソースで確かめています。
前提にした現行実装
図を読み込み中…
ブラウザ側のチェック
| エディタ | 受け付ける形式 | 判定 | 合わないとき |
|---|---|---|---|
リッチテキストエディタ(rt-editor) | image/jpeg・image/png・image/gif | MIME タイプと拡張子(.jpg・.jpeg・.png・.gif)の両方 | 何も起きない |
Markdown エディタ(markdown-field) | 上の 3 つ+image/webp・image/bmp | MIME タイプだけ。ファイル選択の accept 属性にも同じリストを使う | 何も起きない |
根拠は richTextEditor.ts#L65-L66・#L559-L565 と markdownField.ts#L15・#L484-L487 です。リッチテキストエディタ(SunEditor)のファイル選択ダイアログ自体はすべての画像形式を選べますが、選んだ後のチェックで 3 形式以外は捨てられます。
リッチテキストエディタは 1.4.21.0、Markdown エディタのこの実装は 1.5.0.0 で入りました。リッチテキストエディタの形式を 3 つに絞った理由は、コミットメッセージやコメントには書かれていません。
どちらのエディタも、形式のチェックの後に $p.validateImageUploadFileSize でファイルサイズを見ます。上限は Hidden 要素 #ImageUploadFileSizeLimit から読み、超えると TooLargeFile のエラーを出します(util.js#L39-L49)。
サーバー側のチェック
BinaryValidators.OnUploadingImage は次の順に確かめます(BinaryValidators.cs#L116-L190)。形式を名指しで絞る処理はありません。
- 契約で添付ファイルが使えるか。
- 中身が空でないか。
BinaryStorage.jsonのImageUploadFileSizeLimit(MB、既定 10)を超えていないか。- Content-Type が
image/で始まるか。 - ImageSharp の
Image.Identifyで画像として読めるか(読めなければIncorrectFileFormat)。 ImageDecodedSizeLimit(MB、既定 256)が正なら、幅 × 高さ × フレーム数 × 4 バイトがそれを超えないか。- テナントの容量と、項目の「画像の挿入を許可」(
AllowImage)。
1.5.1.0 では 4 の Content-Type のチェックしかありませんでした。5・6 は 1.5.8.1 までに入ったもので、ImageSharp が読めない形式(SVG・AVIF)はここで拒否されます。
保存・サムネイル・表示
| 処理 | 内容 |
|---|---|
| 画像処理のライブラリ | SixLabors.ImageSharp 3.1.12(Implem.Pleasanter.csproj#L83)。JPEG・PNG・GIF・BMP・WebP・TIFF・TGA・PBM を読み書きでき、SVG(ベクター)と AVIF は扱えない |
| リサイズ・サムネイル | Image.Load<Rgba32> で読み、PngEncoder で保存する。サムネイルは常に PNG(ImageData.cs#L52、#L184) |
| 配信時の Content-Type | サムネイルは image/png。元画像は保存時の Content-Type(FileContentResults.cs#L297-L305) |
| ブラウザで開く形式 | BinaryStorage.json の BrowserAllowMimeTypes(既定は PDF・テキスト・JPEG・PNG・GIF・WebP)。BMP は入っていないので、show ではダウンロードになる(BinaryStorage.json#L33-L40) |
改修の範囲
| 形式 | 優先度 | ブラウザ側 | サーバー側 | 設定 |
|---|---|---|---|---|
| WebP | 高 | リッチテキストエディタのリストに足す | 変更不要(ImageSharp が読める) | 変更不要 |
| BMP | 高 | リッチテキストエディタのリストに足す | 変更不要 | ブラウザで開くなら BrowserAllowMimeTypes に image/bmp |
| SVG | 中 | 両エディタのリストに足す | Image.Identify を通らないので、SVG だけ別の経路で保存する処理と、スクリプトを取り除くサニタイズが要る | 同上 |
| AVIF | 低 | 両エディタのリストに足す | ImageSharp 3 は非対応。別のライブラリが要る | 同上 |
WebP・BMP(ブラウザ側だけ)
// richTextEditor.ts
const VALID_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/bmp'];
const VALID_IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp'];// BinaryStorage.json(BMP をブラウザで開く場合)
"BrowserAllowMimeTypes": [
"application/pdf",
"text/plain",
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/bmp"
]BrowserAllowMimeTypes はパラメータなので、本体を改修しなくても変えられます。
SVG の課題
| 課題 | 内容 |
|---|---|
| ImageSharp で読めない | Image.Identify で IncorrectFileFormat になり、リサイズ・サムネイルも作れない。SVG のときは ImageSharp を通さずにそのまま保存する分岐が要る |
| XSS | SVG は XML で、<script> や onload 属性で JavaScript を埋め込める。保存前に DOMPurify などで取り除く必要がある |
| サムネイル | ?thumbnail=1 で PNG を返す仕組みに乗らないので、SVG は元画像をそのまま返すなどの扱いを決める |
AVIF の課題
ImageSharp 3 は AVIF を読み書きできません。対応するには別のライブラリ(libavif のバインディングなど)を組み込むか、ImageSharp の対応を待つ必要があります。