WebMatrix でファイルのアップロード(3) - FileUpload ヘルパーを使う
執筆日時:
まずはお詫びを。
ASP.NET Web Helpers Library という NuGet をインストールすると、(FileUpload ヘルパーを利用して)複数ファイルのアップロードに対応した Form タグを簡単に生成できる。
でも、個人的にはあんまり好きじゃなかったので今回は使わなかった。なんか動的に生成されるノードの名前がカブってるし、あんまりよくわかんなかった。
そしたらツッコミをもらった。
だるさん、HTML の name 属性の値は重複してもいいんやで
— しばやん (@shibayan) 2012年8月19日
最初はなんのことかと思ったけど、 HttpFileCollection は NameObjectCollectionBase を継承している。 NameObjectCollectionBase は重複した複数のキーをもてるので、キーで値を取ろうとすると取りこぼしが発生する、ということみたい。
確かにせやな。 Key はひとつだけど、 Value は複数あるわ。
というわけで、値をすべて取得する拡張メソッド(~/App_Code/HttpFileCollectionBaseExtension.cs)は
using System.Collections.Generic; using System.Web;public static class HttpFileCollectionBaseExtension { public static IEnumerable<HttpPostedFileBase> ToEnumerable( this HttpFileCollectionBase target) { foreach (var key in target.AllKeys) //–> Key で…… { yield return target[key]; } } }
ではなくて、
using System.Collections.Generic; using System.Web; public static class HttpFileCollectionBaseExtension { public static IEnumerable<HttpPostedFileBase> ToEnumerable( this HttpFileCollectionBase target) { for (int i = 0; i < target.Count; i++) //--> Index で! { yield return target[i]; } } }
じゃないとダメみたい。 for 文なんて久しぶりに書いたわ……。
んで、 Default.cshtml をこんな感じで書いてみた。
<!DOCTYPE html> @{ IEnumerable<dynamic> model = null; if (IsPost) { model = Request.Files.ToEnumerable() .Select<HttpPostedFileBase, dynamic>((file) => { try { var path = "~/Files/" + file.FileName; file.SaveAs(Server.MapPath(path)); return new { Result = "Success", Src = VirtualPathUtility.ToAbsolute(path), Message = string.Format( "{0} is saved successfully", file.FileName), }; } catch (Exception e) { return new { Result = "Error", Message = e.Message, }; } }); } } <html lang="ja"> <head> <meta charset="utf-8" /> <title>マイ サイトのタイトル</title> <style> html { font-family: Meiryo, sans-serif; } .label { color: #fff; font-size: 0.8em; border-radius: 2px; padding: 0 5px; } .success { background-color: #0094ff; } .error { background-color: #ff6a00; } </style> </head> <body> <h1>File Upload Test</h1> <h2>Upload</h2> @FileUpload.GetHtml() <h2>Result</h2> @if (model == null) { <p>No files are uploaded.</p> } else { <ul> @foreach (var item in model) { <li><span class="label @item.Result.ToLower()"> @item.Result</span> @item.Message</li> } </ul> } </body> </html>
結果はこんな感じ。
できた!