ASP.NET Core でリッチテキストエディターに画像ギャラリーを追加する

アップロードボタンでできるのは、ファイルを追加することだけです。「Insert Gallery」ダイアログでは、すでにサーバーにあるものを表示し、フォルダーを開いたり、新しいフォルダーを作成したり、開いているフォルダーにアップロードしたりできます。ASP.NET Core でこのダイアログを使うには、フォルダーの一覧を JSON で返す GET ルート、新しいフォルダー用の POST ルート、そしてアップロード用のルートが必要です。このページでは、一覧の作成とファイルの配信方法を説明します。
ボタンとオプション
full ツールバーには、すでに「Insert Gallery」ボタン(insertgallery)があります。galleryEndpoint を設定すると、ダイアログは固定のプリセット画像の一覧ではなく、サーバーの画像を表示するようになります。
var editor = new RichTextEditor("#editor", {
toolbar: "full",
galleryEndpoint: "/rte/gallery",
maxUploadFileSize: 4000000 // same limit as the server, checked before the upload starts
});ダイアログは、フォルダーを一覧表示するときにはその URL に ?folder= を付け、「New folder」ボタンでは同じ URL に action=create-folder を POST します。アップロードは rte-files.js を通じて /rte/upload に送られます。
ルート
// GET /rte/gallery?folder=products
[HttpGet("gallery")]
public IActionResult Gallery([FromQuery] string? folder) =>
Send(RteFiles.List(HttpContext, folder));最小 API のサンプルでは、同じ処理は rte.MapGet("/gallery", (HttpContext http, string? folder) => ...) の 1 行です。どちらも RteFiles.List を呼び出します。
// GET /rte/gallery?folder=products
public static RteResult List(HttpContext http, string? folder) => Run(() =>
{
var root = RootFor(http);
folder ??= "";
return Listing(root, folder, Resolve(root, folder));
});Resolve は、仮想フォルダーを uploads/ 内のパスに変換するか、400 または 404 を返します。これについては ASP.NET Core のギャラリーフォルダーで説明しています。
ダイアログが読み取る JSON
static RteListing Listing(RteRoot root, string folder, string disk)
{
var dir = new DirectoryInfo(disk);
var folders = dir.EnumerateDirectories()
.Where(d => IsValidName(d.Name))
.OrderBy(d => d.Name.ToLowerInvariant(), StringComparer.Ordinal)
.Select(d => new RteFolder(folder.Length == 0 ? d.Name : folder + "/" + d.Name, d.Name))
.ToList();
var images = dir.EnumerateFiles()
.Where(f => !f.Name.StartsWith('.') && Extensions.Contains(f.Extension.ToLowerInvariant()))
.OrderBy(f => f.Name.ToLowerInvariant(), StringComparer.Ordinal)
.Select(f => new RteImage(UrlFor(root, folder, f.Name), f.Name, folder, f.Length))
.ToList();
string? parent = folder.Length == 0 ? null : folder.Contains('/') ? folder[..folder.LastIndexOf('/')] : "";
return new RteListing(folder, "/" + folder, parent, folders, images);
}
- ルートでは
parentFolderがnullになり、ダイアログの「Up」ボタンが無効になります。フォルダー内では親の仮想パスで、最上位のフォルダーなら""です。 - プロパティがキャメルケースで出力されるのは、
Results.Jsonでも MVC のObjectResultでも、ASP.NET Core の JSON の既定がキャメルケースだからです。 - 一覧に含めるのは 5 種類の画像だけです。
.gitのように、名前がフォルダーのルールに合わないフォルダーはスキップします。 - 名前は小文字にした形を序数比較で並べ替えます。
StringComparer.OrdinalIgnoreCaseは大文字にして比較するため、a_bとaabの順序がブラウザーと異なってしまいます。 - URL の各セグメントは
Uri.EscapeDataStringでエスケープするので、My photosというフォルダーでも正しく機能する URL になります。
画像の配信
URL は /uploads/ を指しており、独自のコンテンツタイプの一覧を持つ 2 つ目の UseStaticFiles の呼び出しで配信されます。
var uploads = RteFiles.UploadsFolder(app.Environment);
Directory.CreateDirectory(uploads);
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(uploads),
RequestPath = "/uploads",
ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
[".jpg"] = "image/jpeg", [".jpeg"] = "image/jpeg", [".png"] = "image/png",
[".gif"] = "image/gif", [".webp"] = "image/webp",
}),
OnPrepareResponse = ctx => ctx.Context.Response.Headers.XContentTypeOptions = "nosniff",
});この一覧が重要です。既定の FileExtensionContentTypeProvider では、フォルダー内の .html や .svg のファイルが、text/html や image/svg+xml として配信されてしまいます。ServeUnknownFileTypes を無効にしても(これが既定です)、ブロックされるのはプロバイダーが知らない拡張子だけです。サンプルの一覧を使うと、uploads/ に置いた probe.html と probe.svg はどちらも 404 になりました。
現在のダイアログの動作
リリース版のエディターのダイアログには「Accepted formats: JPG, JPEG, JFIF, PNG, GIF, WEBP, BMP, AVIF」と表示されます。このサーバーが受け付けるのはそのうち 5 つです。.bmp は送信されますが、サーバーは 400 を返し、ダイアログはサーバーのメッセージではなく、独自の「Upload failed for pic.bmp.」を表示します。maxUploadFileSize を超えるファイルは、ブラウザーで「big.jpg is 4.3 MB. The maximum allowed size is 3.8 MB.」として拒否されます。ダイアログは 2 進数のメガバイトで計算します。
テストする
このガイドのサンプルプロジェクトを dotnet run で実行し、http://localhost:5302 を開いて「Insert Gallery」をクリックします。ダイアログを使わずに JSON を確認するには、以下を実行します。
curl "http://localhost:5302/rte/gallery?folder=blog%2F2026"
{"currentFolder":"blog/2026","currentFolderDisplay":"/blog/2026","parentFolder":"blog","folders":[],"images":[]}このフォルダーは「New folder」ボタンで作成したものです。存在しないフォルダーを指定すると、404 {"error":"Folder not found."} が返ります。
ほかのプラットフォーム向けの同じガイド
JavaScript · Next.js · PHP · Razor Pages · Blazor · MVC 5 · Web Forms