.NET Core で画像を縮小する

ふと思い立って、DS用のツイッタークライアントを試作している。ツイッター社からの機能の公開が不十分だし、それぞれのクライアント開発者も続々と撤退しているので、メリットがあるのか?というえば「ない!」んだけど、ASP.NET MVC Core の周辺の調査も兼ねて、というところで。

いずれ、github にでも公開するが、

  • ASP.NET MVC Core + .NET Core
  • CoreTweet

の組み合わせで作っている。以前、作っていた時は PHP でやっていたのだが、PHP にあまり慣れていないところもあって頓挫していしまった。今回は、仮想サーバーも借りたことだし、.NET Core も入れれば、その界隈のライブラリを使えるというメリットがあって、この組み合わせになっている。

.NET Core で画像を扱う

DSブラウザ(写真のはDSiなんだけど)は、結構古いので、絵文字が使えないとか内蔵している証明書が古めとか、そういう問題もある。メモリが限られているというのもある。なので、大き目の画像をはめてしまうとブラウザのメモリから溢れてしまう。

ひとまず、ツイッターで表示されている画像を 320×240 に固定にしてしまおう。

ところで、.NET Core 2.0 には画像関係のライブラリは標準で付いてこない。.NET Standard 3.0 だったかにはその予定はあるのだけど、ひとまず現状はない。

.NET Core向けの画像ライブラリ
https://www.infoq.com/jp/news/2017/04/net-core-imaging

最終的には、ラズパイや外部Linuxサーバーで動かすことになるので、Windows依存にしたくないところなので、.NET Core のままいくとして、NuGet から System.Drawing.Common をダウンロードする。

using System.Drawing;
using System.Net.Http;

public class ImageController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
    [HttpGet("Image/{path}")]
    public IActionResult Sumnail([FromRoute] string path)
    {
        try
        {
            var hc = new HttpClient();
            path = System.Web.HttpUtility.UrlDecode(path);
            var res = hc.GetAsync(path, HttpCompletionOption.ResponseContentRead).Result;
            var st = res.Content.ReadAsStreamAsync().Result;
            var srcbmp = new Bitmap(st);
            int h = 240;
            int w = 320;

            if ( srcbmp.Width > 320 )
            {
                h = srcbmp.Height * w / srcbmp.Width;
                if ( h > 240 )
                {
                    w = w * 240 / h;
                    h = 240;
                }
            }
            if (srcbmp.Height > 240)
            {
                w = srcbmp.Width * h / srcbmp.Height;
                if (w > 320)
                {
                    h = h * 320 / w;
                    w = 320;
                }
            }
            var destbmp = new Bitmap(w, h);
            var g = Graphics.FromImage(destbmp);
            g.FillRectangle(new SolidBrush(Color.LightGray), 0, 0, w, h);
            g.DrawImage(srcbmp, 0, 0, w, h);
            var mem = new System.IO.MemoryStream();
            destbmp.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg);
            mem.Position = 0;

            return new FileStreamResult(mem, "image/jpeg");
        }
        catch
        {
            return NotFound();
        }
    }
}

こうすると、

http://localhost:5000/Image/http%3a%2f%2fpbs.twimg.com%2fmedia%2fDfa0dCxUEAAgrwg.jpg

な感じで、320×240 の画像に縮小できる。これをタイムラインの index.cshtml に埋め込む。

@foreach ( var it in statuses )
{
<div style="width:320px;">
    @{
        var item = it.RetweetedStatus == null ? it : it.RetweetedStatus;
    }
    @if (it.RetweetedStatus != null)
    {
        <div><a href="Tweet/@it.User.ScreenName">@it.User.Name</a> さんがリツイート</div>
    }
    <img src="@item.User.ProfileImageUrl" width="48" height="48" align="left" style="margin:4px" />
    <div style="font-weight:bold"><a href="Tweet/@it.User.ScreenName">@item.User.Name</a> @item.User.ScreenName @item.CreatedAt</div>
    <div>@item.Text</div>

    @if (item.Entities != null && item.Entities.Media != null)
    {
        @foreach (var m in item.Entities.Media)
        {
            <!-- TODO:画像はサムネール化する -->
            var thum = System.Web.HttpUtility.UrlEncode(m.MediaUrl); 
            <img src="/Image/@thum" />
        }
    }
</div>

多分、magic.net を使ったほうが高機能なんだろうけど、縮小だけだったらこれで十分かも。Graphics クラスの中身はひと通りあるみたい。

NuGet Gallery | System.Drawing.Common 4.5.0
https://www.nuget.org/packages/System.Drawing.Common/

つい最近(5/30)に正式版になったっぽい

カテゴリー: 開発, C#, NET Core パーマリンク