ASP.NET MVC ファイルアップロード
概要
久しぶりにASP.NET MVCです。以前いくつかMVC2についてポストしましたが、いい加減Razor使いたい!ということでASP.NET MVC5にしてみました。まったく仕事でMVC5を使う予定はありませんがね!
それはさておき、ファイルのアップロードです。最初に完成イメージ。
環境
Visual Studio Community 2015 Update 2
ASP.NET MVC5
実装
アップロードする画面と結果を表示する画面に対応したコントローラー、モデル、ビューで構成されています。
コントローラー
ファイルをアップロード後にファイル一覧を表示する画面にリダイレクトしています。
Controllers\HomeController.cs
[HttpGet] public ActionResult Upload() { return View(); } [HttpPost] public ActionResult Upload(UserData inputModel) { var destinationFolder = Server.MapPath("~/Users"); if (!Directory.Exists(destinationFolder)) { Directory.CreateDirectory(destinationFolder); } var postedFile = inputModel.Picture; if (postedFile.ContentLength > 0) { var fileName = Path.GetFileName(postedFile.FileName); var path = Path.Combine(destinationFolder, fileName); postedFile.SaveAs(path); } return RedirectToAction("FileIndex", "Home"); } [HttpGet] public ActionResult FileIndex() { var destinationFolder = Server.MapPath("~/Users"); string[] files = Directory.GetFiles(destinationFolder, "*", SearchOption.AllDirectories); ViewBag.files = files; return View(); }
モデル
アップロードファイルのモデルバインディングに対応するにはHttpPostedFileBase型として宣言する必要があります。
Models\UserData.cs
public class UserData { public string Name { get; set; } public string Email { get; set; } public HttpPostedFileBase Picture { get; set; } }
ビュー
(念願のRazorです!)
ファイルアップロードするにはformタグにenctype = "multipart/form-data"が必要です。
Views\Home\Upload.cshtml
@using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>UserData</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Email, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Email, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Picture, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> <input type="file" id="picture" name="picture" class="form-control" /> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> }
結果はViewBagに入れて表示するだけ。
Views\Home\FileIndex.cshtml
@foreach (var file in ViewBag.files) { <div>@file</div> }
実行結果
Pictureを選択して[Create]ボタンを押下すると…
保存されました!これはAzure App ServiceのWebサイトにアップした結果です。こんなパスになるんですね。
Kuduでも確認してみました(初めて使ってみる)。ちゃんと保存されているようです。
実際にはセキュリティだとかエラーハンドリングだとかもっとしっかりやらないとダメですよ!(‘А`)
Server.MapPathについて
参考書には以下のように指定されてまして、この通りでもローカルで実行した場合はうまく動きました。
Server.MapPath("/Users")
ただ、IISにデプロイするときにアプリケーション名(このサンプルではvoyager02)を指定するとパスが通らなくなってしまいました。
これは/(スラッシュ)を指定するとWebサイト(ドメインのルート)の物理パスを取得するため一個上の階層になってるんですね。また、ドメインのルートとアプリケーションのルートは必ずしも階層関係にある必要はないため余計にややこしいですね。
Webサイト直下に配置するのであればスラッシュでもいいのかもしれませんが、ルート演算子(~)をつけてあげるとアプリケーションルートの物理パスが取れるので、アプリケーション名に依存しなくなります。
Server.MapPath("~/Users")
参考
プログラミングASP.NET MVC 第3版 ASP.NET MVC 5対応版
- 作者: ディノエスポシト
- 出版社/メーカー: 日経BP社
- 発売日: 2015/11/18
- メディア: Kindle版
- この商品を含むブログを見る