【ASP.NET(C#)9】PostgreSQLでプレビュー付き画像投稿/ポップアップで拡大表示機能を実装(.NET 6)

今回はWebアプリケーションでは定番の画像を扱った機能をASP.NET Core MVC(.NET 6)+ EntityFrameworkCore(PostgreSQL)で実装したいと思います。

今回はHeroku公開での流れを汲んでPostgreSQLを使用します。CRUD画面とPostgreSQLでのインストール/Nugetパッケージ等の初期設定は終わっているものとして進めますのでもしわからない方は過去記事をご参照ください。

DB側、Model側でデータを用意

PostgreSQLに画像が入るテーブルをインサート

今回は画像のタイトルと画像の本体を入れられるテーブルを以下のように作成します。

CREATE TABLE imageentity (
    id      serial   primary key,
    imagetitle text     not null,
    image   bytea       not null,
    created timestamp   not null,
    updated timestamp   not null
)

PostgreSQLでは画像を入れる型は bytea になります。

エンティティの実装

対するEntityFrameworkで使うEntityの宣言は下記のようにします。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ImageSaveApp.Models
{
    public class ImageEntity
    {
        [Column("id")]
        public int Id { get; set; }

        /// <summary>
        /// 画像ファイル名
        /// </summary>
        [Required]
        [Display(Name = "ファイル名")]
        [Column("imagetitle")]
        public string ImageTitle { get; set; }

        /// <summary>
        /// 画像イメージ
        /// </summary>
        [Display(Name = "画像")]
        [Column("image")]
        public byte[] Image { get; set; }

        /// <summary>
        /// 生成日
        /// </summary>
        [Display(Name = "生成日")]
        [Column("created")]
        public DateTime Created { get; set; }

        /// <summary>
        /// 更新日
        /// </summary>
        [Display(Name = "更新日")]
        [Column("updated")]
        public DateTime Updated { get; set; }
    }
}

画像を入れる時の型は byte[] になります。

実装の流れ

画像投稿(アップロード)できるようにする

一番簡単な方法は 下記のようにHTMLのinput type に fileを指定することです。

index.cshtml

<form method="post" enctype="multipart/form-data" asp-action="Create">
    <div class="form-file">
        @*画像アップロード用*@
		<input type="file" class="form-file-input" id="image" name="formFile"  accept=".jpg,.png,.gif"/>
    </div>
    <div>
        @*登録ボタン*@
        <input type="submit" value="登録" name="btnname"> 
    </div>
</form>

これだけで上のようなファイルアップロードボタンができてしまいました。コントロール側は下記のようにすればDBに登録できます。画像の部分の取り方が少し特殊ですよね。

HomeController.cs

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(IFormFile formFile)
{
    var image = new ImageEntity();

    if (ModelState.IsValid)
    {
        using var ms = new MemoryStream();
        {
            formFile.CopyTo(ms);
            image.Image = ms.ToArray();
        }
        image.ImageTitle = formFile.FileName;
        image.Created = DateTime.UtcNow.AddHours(9);
        image.Updated = DateTime.UtcNow.AddHours(9);

        _context.Add(image);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }

    return View(image);
}

表示側を実装

次にindex.cshtmlの表示側を以下のようにします。

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.image.Id)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.image.ImageTitle)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.image.Image)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.images) {
        <tr>
            <td>
                @item.Id
            </td>
            <td>
                @item.ImageTitle
            </td>
            <td>
                <img style="width: 300px;" src="data:image;base64,
                    @System.Convert.ToBase64String(item.Image)">
            </td>
        </tr>
        }
    </tbody>
</table>

大事なのは26,27の画像部分で src指定にBase64化したitem.Imageを渡してあげることです。


これだけで登録したものがすぐ表示されるものができました。この後この機能を拡張していきます。

動画選択後のプレビュー機能を入れる

上の登録動画のように、ファイルを選択したらその画像が正しいかプレビューを表示してくれると嬉しいですよね?

今のところ上記のソースではプレビューは表示されません。画像登録側のindex.cshtmlを以下のように変更します。 変更追加点にマーキングしています。

index.cshtml

<form method="post" enctype="multipart/form-data" asp-action="Create">
    <div class="form-file">
        <p class="change-image">
            <img id="preview" style="width: 150px;"/>
        </p>
        @*画像アップロード用*@
		<input type="file" class="form-file-input" id="image" name="formFile" onchange="previewImage(this)" accept=".jpg,.png,.gif"/>
    </div>
    <div>
        @*登録ボタン*@
        <input type="submit" value="登録" name="btnname"> 
    </div>
</form>

~データ表示処理(今回は省略)~

@section Scripts {
    <script>
        function previewImage(obj)
        {
	        let fileReader = new FileReader();
	        fileReader.onload = (function() {
		        document.getElementById('preview').src = fileReader.result;
	        });
	        fileReader.readAsDataURL(obj.files[0]);
        }
    </script>
}
  • p class=”change-image”と空のid=”preview”のimgを 定義
  • input type=”file” に onchange=”previewImage(this)” を置いて JavaScript のfunctionを呼ぶようにする
  • JavaScript側で受け取ったファイルを読み取って ‘preview’のsrcに書き込む

これで選択画像の中身が変更される毎にプレビューエリアに画像が表示されるようになります。

投稿された画像をクリックしたらポップアップで拡大する

次は一覧の画像がクリックされたら下の画像ように拡大してポップアップっぽく表示されるようにします。こちらも最近のサイトでよく見られる手法ですよね。

一見難しそうですがこちらはJQueryとLightbox2を定義してあげれば簡単に実装できます。

jqueryとlightboxを入れる

というわけでlightboxを使えるようにするには2つを入れるために下記コードを入れなければいけません。

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/css/lightbox.css" />
<script src="https://code.jquery.com/jquery-1.12.4.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/js/lightbox.min.js"></script>

ただし、ASP.NET MVC Core のテンプレートで作成した場合は、最初からJQueryがインストールされているため、他の二つだけでOKです(.NET 6 の場合) 万が一過去バージョンで動かなかった場合はプリインストールされている JQuery のバージョンが古い可能性が高いので更新した方がいいですね。

ASP.NETの場合、上記コードをViews/Shared/_Layout.cshtml に入れます。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - ImageSaveApp</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/ImageSaveApp.styles.css" asp-append-version="true" />

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/css/lightbox.css" />
</head>

~途中は省略~

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/js/lightbox.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

これらを入れたら後は表示側を下記のように修正すれば完了です。

・修正前
<img style="width: 300px;" src="data:image;base64,@System.Convert.ToBase64String(item.Image)">
・修正後
<a href="data:image;base64,@System.Convert.ToBase64String(item.Image)" data-lightbox="group"><img style="width: 300px;" src="data:image;base64,
                    @System.Convert.ToBase64String(item.Image)">

<a hrefの指定とdata-lightbox=”group”を指定すれば完了です。このコードの良い所は画像ファイルをフォルダ格納タイプでなく、データベース格納タイプで問題なく動作できることですね。

AWS/AzureやレンタルサーバPHPを利用した場合はストレージがあるので直接画像ファイルを格納すればいいですが、Herokuだとストレージがなく、直接のファイル格納はできないのでありがたいです。

参考にしたサイト

【ASP.NET Core】C# 画像ファイルをBLOBでDBに登録する方法

ブラウザで画像ファイルを選択した時にプレビュー表示する方法

HTMLページの画像をクリックしたら拡大&ポップアップウィンドウで表示させる方法

<input type=”file”>

まとめ

画像を扱う技術に関しては難易度高そうでしたが、Webの定番のおかげか思った程ではなく、参考になるサイトを頼りに左程苦戦せず実装できました。

まぁASP.NETに関する記載は上記貼り付けた参考サイトのみでしたが今までの経験からなんとなくこうすればいいみたいなのがわかってきたので確実に技術力はアップしていますね。

何かの手助けになれば幸いです。