项目需要,做一个图片上传的功能,本来是很简单,但是需要同时上传多个文件,并分条带一些额外的信息,听上去很复杂,通过下面图就可以一目了然:

网上找过一些方法,但多为不支持图片与其他信息关联,或者分两次上传(文件一次,返回一个indicator,然后通过json传其他信息),不理想。偶然发现一个MVC的方法,思路非常简单,就是MVC,独特之处是把图片作为一个HttpPostedFileBase类型的属性存到ViewModel里。直接看代码:

<form action="" method="post" enctype="multipart/form-data">
@for (int i = ; 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[];
var validImageTypes = new string[] { "image/gif", "image/jpeg", "image/pjpeg", "image/png" }; if (model.ImageUpload == null || model.ImageUpload.ContentLength == )
{
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 > )
{
//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

原文链接

发现原来还可以这样,但是由于我使用的knockout,所以提交数据需要用JS完成,需要将其转换,但当我构建完对象,将其转换为JSON的时候,发现文件不是随便就能序列化的。观察上面例子提交的请求:

跟普通的Form相比,并无特殊之处,说明只要content-type为mutipart/form-data,Form的name按照序号加名称的格式填写,Action就能获得到指定的数据,我将代码改为这种形式:

<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,说明推论合理。然后做进一步修改:

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().files[]); 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().files[]);
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自己修改数据格式。

到这里本来问题就解决了,但是,但是,IE9及以下不支持FormData,于是做了进一步修改,以来表单的提交功能,JS里构建需要的<input type="hidden" />,设置好name属性,然后提交表单$("#Form").submit(),思路就是这样,代码就不写了。

其实,这里的原理我还是不大清楚,为什么action能将这样的一个name list还原为对象,我猜跟negotiation有关系,还需要进一步研究。

MVC 文件上传的更多相关文章

  1. MVC文件上传-使用jQuery.FileUpload和Backload组件实现文件上传

    本篇使用客户端jQuery-File-Upload插件和服务端Badkload组件实现多文件异步上传.MVC文件上传相关兄弟篇: 处理文件上传的服务端组件Backload 用于处理文件上传的服务端组件 ...

  2. ASP.NET MVC 文件上传和路径处理

    ASP.NET MVC 文件上传和路径处理总结 目录 文件的上传和路径处理必须解决下面列出的实际问题: 1.重复文件处理 2.单独文件上传 3.编辑器中文件上传 4.处理文章中的图片路径 5.处理上传 ...

  3. MVC文件上传与下载

    MVC文件上传与下载 MVC文件上传与下载 想想自己从毕业到工作也有一年多,以前公司的任务的比较重,项目中有的时候需要用到什么东西都去搜索一下,基础知识感觉还没有以前在学校中的好.最近开始写博客,真的 ...

  4. Spring MVC 笔记 —— Spring MVC 文件上传

    文件上传 配置MultipartResolver <bean id="multipartResolver" class="org.springframework.w ...

  5. MVC文件上传09-使用客户端jQuery-File-Upload插件和服务端Backload组件让每个用户有专属文件夹,并在其中创建分类子文件夹

    为用户创建专属上传文件夹后,如果想在其中再创建分类子文件夹,该怎么做?可以在提交文件的视图中再添加一个隐藏域,并设置 name="uploadContext". 相关兄弟篇: MV ...

  6. MVC文件上传08-使用客户端jQuery-File-Upload插件和服务端Backload组件让每个用户有专属文件夹

    当需要为每个用户建立一个专属上传文件夹的时候,可以在提交文件的视图中添加一个隐藏域,并设置name="objectContext". 相关兄弟篇: MVC文件上传01-使用jque ...

  7. MVC文件上传07-使用客户端jQuery-File-Upload插件和服务端Backload组件裁剪上传图片

    本篇通过在配置文件中设置,对上传图片修剪后保存到指定文件夹. 相关兄弟篇: MVC文件上传01-使用jquery异步上传并客户端验证类型和大小  MVC文件上传02-使用HttpPostedFileB ...

  8. MVC文件上传06-使用客户端jQuery-File-Upload插件和服务端Backload组件自定义控制器上传多个文件

    当需要在控制器中处理除了文件的其他表单字段,执行控制器独有的业务逻辑......等等,这时候我们可以自定义控制器. MVC文件上传相关兄弟篇: MVC文件上传01-使用jquery异步上传并客户端验证 ...

  9. MVC文件上传05-使用客户端jQuery-File-Upload插件和服务端Backload组件自定义上传文件夹

    在零配置情况下,文件的上传文件夹是根目录下的Files文件夹,如何自定义文件的上传文件夹呢? MVC文件上传相关兄弟篇: MVC文件上传01-使用jquery异步上传并客户端验证类型和大小  MVC文 ...

  10. MVC文件上传04-使用客户端jQuery-File-Upload插件和服务端Backload组件实现多文件异步上传

    本篇使用客户端jQuery-File-Upload插件和服务端Badkload组件实现多文件异步上传.MVC文件上传相关兄弟篇: MVC文件上传01-使用jquery异步上传并客户端验证类型和大小  ...

随机推荐

  1. healthkit 记录每天用户的运动情况

    //详细操作步骤 http://www.csdn.net/article/2015-01-23/2823686-healthkit-tutorial-with-swift //官方api https: ...

  2. C#堆栈原理(我有两个例子测试你到底会不会)

    背景 上次写了一篇文章关于try finnally的一些疑问(被我用windows live覆盖了,草),后来经过大神们解释,我明白了在我理解了try.finnally运行原理后,还欠缺的就是关于值类 ...

  3. 华硕_ZX50JX4200 安装ssd固态盘

    本人亲身的一次经历,帮朋友的华硕手提装一个内存和ssd固态 内存5分钟搞定,但是ssd固态盘就经过了一番的折腾 首先要拧掉所有后盖的螺丝,把光驱拆下来,注意撬开键盘板的时候有排线,不能弄断了.然后一定 ...

  4. ExtJs4 学习3 combox自动加载的例子

    Ext.onReady(function() {   delivery_datas = [{ "Id" : "1", "Name" : &q ...

  5. wdcp对default站点开启apache url重写功能

    网站开启对default网站的重写功能

  6. mysql数据类型——时间类型

    四种日期格式: 每个时间类型有一个有效值范围和一个"零"值,当指定不合法的MySQL不能表示的值时使用"零"值. YEAR         0000 YYYY ...

  7. 在 LINQ to Entities 查询中无法构造实体或复杂类型

    public List<CustomerType> GetCustomerTypesBySchemaTypeCode(int schemaTypeCode) { var query = ( ...

  8. Objective-c之NSCopying

    Objective-c之NSCopying     copy的原理: 执行<NSCopying>协议,类中必须实现copyWithZone:方法响应的copy消息. copy消息将发送co ...

  9. BZOJ 1072 排列

    Description 给一个数字串\(s\)和正整数\(d\), 统计\(s\)有多少种不同的排列能被\(d\)整除(可以有前导\(0\)).例如\(123434\)有\(90\)种排列能被\(2\ ...

  10. BZOJ 1046 上升序列

    Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ...