MVC 文件上傳

項目須要,作一個圖片上傳的功能,原本是很簡單,可是須要同時上傳多個文件,並分條帶一些額外的信息,聽上去很複雜,經過下面圖就能夠一目瞭然:ajax

 

網上找過一些方法,但多爲不支持圖片與其餘信息關聯,或者分兩次上傳(文件一次,返回一個indicator,而後經過json傳其餘信息),不理想。偶然發現一個MVC的方法,思路很是簡單,就是MVC,獨特之處是把圖片做爲一個HttpPostedFileBase類型的屬性存到ViewModel裏。直接看代碼:json

<form action="" method="post" enctype="multipart/form-data">
        @for (int i = 0; i < Model.Count; i++)
            {
            <div class="col-xs-11 row space-top">
                <div class="col-xs-2">
                    @Html.EditorFor(m => m[i].Title)
                </div>
                <div class="col-xs-2">
                    @Html.EditorFor(m => m[i].AltText)
                </div>
                <div class="col-xs-2">
                    @Html.EditorFor(m => m[i].Caption)
                </div>
                <div class="col-xs-3">
                    @Html.TextBoxFor(m => m[i].ImageUpload, new { type = "file" })
                </div>
            </div>
        }
        <div class="container space-top" >
            <button class="btn col-xs-2 space-top" type="submit">Create</button>
        </div>
    </form>
View
// GET: Image
        public ActionResult Create()
        {
            var models = new List<ImageViewModel>();
            models.Add(new ImageViewModel());
            models.Add(new ImageViewModel());
            return View(models);
        }
Controller
public class ImageViewModel
    {
        [Required]
        public string Title { get; set; }

        public string AltText { get; set; }

        [DataType(DataType.Html)]
        public string Caption { get; set; }

        [DataType(DataType.Upload)]
        public HttpPostedFileBase ImageUpload { get; set; }
    }
ViewModel
[HttpPost]
        public ActionResult Create(List<ImageViewModel> models)
        {
            var model = models[0];
            var validImageTypes = new string[] { "image/gif", "image/jpeg", "image/pjpeg", "image/png" };

            if (model.ImageUpload == null || model.ImageUpload.ContentLength == 0)
            {
                ModelState.AddModelError("ImageUpload", "This field is required");
            }
            else if (!validImageTypes.Contains(model.ImageUpload.ContentType))
            {
                ModelState.AddModelError("ImageUpload", "Please choose either a GIF, JPG or PNG image.");
            }
            if (ModelState.IsValid)
            {
                var image = new Image
                {
                    Title = model.Title,
                    AltText = model.AltText,
                    Caption = model.Caption
                };

                if (model.ImageUpload != null && model.ImageUpload.ContentLength > 0)
                {
                    //var uploadDir = "~/uploads";
                    //var imagePath = Path.Combine(Server.MapPath(uploadDir), model.ImageUpload.FileName);
                    //var imageUrl = Path.Combine(uploadDir, model.ImageUpload.FileName);
                    //model.ImageUpload.SaveAs(imagePath);
                    //image.ImageUrl = imageUrl;
                }
                //db.Create(image);
                //db.SaveChanges();
               // return RedirectToAction("Index");
            }

            return View(models);
        }
Post Action

 

原文連接mvc

發現原來還能夠這樣,可是因爲我使用的knockout,因此提交數據須要用JS完成,須要將其轉換,但當我構建完對象,將其轉換爲JSON的時候,發現文件不是隨便就能序列化的。觀察上面例子提交的請求:app

跟普通的Form相比,並沒有特殊之處,說明只要content-type爲mutipart/form-data,Form的name按照序號加名稱的格式填寫,Action就能得到到指定的數據,我將代碼改成這種形式:ide

<form action="Create" encType="multipart/form-data" method="post">

    <div>
        <label for="">Title</label>
        <input name="[0].Title" type="text" value="a">
    </div>
    <div>
        <label for="">AltText</label>
        <input name="[0].AltText" type="text" value="a">
    </div>
    <div>
        <label for="">Caption</label>
        <input name="[0].Caption" type="text" value="a">
    </div>
    <div>
        <label for="">ImageUpload</label>
        <input name="[0].ImageUpload" type="file">
    </div>
    <div>
        <label for="">Title</label>
        <input name="[1].Title" type="text" value="b">
    </div>
    <div>
        <label for="">AltText</label>
        <input ame="[1].AltText" type="text" value="b">
    </div>
    <div>
        <label for="">Caption</label>
        <input name="[1].Caption" type="text" value="b">
    </div>
    <div>
        <label for="">ImageUpload</label>
        <input name="[1].ImageUpload" type="file">
    </div>

    <button type="submit">Create</button>
</form>
HTML

依然work,說明推論合理。而後作進一步修改:post

function mySubmit() {
    var formData = new FormData();
    formData.append("[0].Title", $("[name='[0].Title'").val());
    formData.append("[0].AltText", $("[name='[0].AltText'").val());
    formData.append("[0].Caption", $("[name='[0].Caption'").val());
    formData.append("[0].ImageUpload", $("[name='[0].ImageUpload'").get(0).files[0]);

    formData.append("[1].Title", $("[name='[1].Title'").val());
    formData.append("[1].AltText", $("[name='[1].AltText'").val());
    formData.append("[1].Caption", $("[name='[1].Caption'").val());
    formData.append("[1].ImageUpload", $("[name='[1].ImageUpload'").get(0).files[0]);
    console.log(formData);

    $.ajax({
        contentType: false,
        url: "/Image/Create",
        type: "POST",
        processData: false,
        dataType: 'json',
        data: formData,
        success: function (result) {

        },
        error: function (err) {
            alert(err.statusText);
        }
    });
}
JS

注意把contentType和processData都設爲false,防止AJAX本身修改數據格式。ui

到這裏原本問題就解決了,可是,可是,IE9及如下不支持FormData,因而作了進一步修改,以來表單的提交功能,JS裏構建須要的<input type="hidden" />,設置好name屬性,而後提交表單$("#Form").submit(),思路就是這樣,代碼就不寫了。url

其實,這裏的原理我仍是不大清楚,爲何action能將這樣的一個name list還原爲對象,我猜跟negotiation有關係,還須要進一步研究。spa

相關文章
相關標籤/搜索