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

网上找过一些方法,但多为不支持图片与其他信息关联,或者分两次上传(文件一次,返回一个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. webpack学习笔记一(入门)

    webpack集成了模块加载和打包等功能 ,这两年在前端领域越来越受欢迎.平时一般是用requirejs.seajs作为模块加载用,用grunt/gulp作为前端构建.webpack作为模块化加载兼容 ...

  2. Servlet(三)

    重定向 服务器向浏览器发送一个302状态码以及一个Location消息头(该消息头的值是一个地址,称之为重定向地址),浏览器收到后会立即向重定向的地址发出请求,使用相应对象的API方法实现(respo ...

  3. C语言字符串库函数的实现

    1.strlen(字符串的长度) size_t Strlen(const char* str) { assert(str); ;; ++i) { if (str[i] == '\0') return ...

  4. 【POJ2886】【线段树】Who Gets the Most Candies?

    Description N children are sitting in a circle to play a game. The children are numbered from 1 to N ...

  5. 【POJ2985】【Treap + 并查集】The k-th Largest Group

    Description Newman likes playing with cats. He possesses lots of cats in his home. Because the numbe ...

  6. 关于Weblogic 10.3.1集群及调优经历

    一.  集群 ·集群易于管理.灵活的负载平衡.较强的安全机制 ·配置前的规划 操作系统 硬件配置 角色 windows IP: 192.168.1.101:7001 AdminServer windo ...

  7. Tomcat虚拟主机配置

    3.1.配置虚拟主机 配置虚似主机就是配置一个网站. 在Tomcat服务器配置一个虚拟主机(网站),需要修改conf文件夹下的server.xml这个配置文件,使用Host元素进行配置,打开serve ...

  8. ps中常用的快捷键

    ctrl+c  复制 ctrl+v 粘贴 ctrl+n 新建文件 ctrl+s 保存 空格键   手抓工具 ctrl+t  自由变形 ctrl+加号  放大 ctrl+减号  缩小 ctrl+r  标 ...

  9. 用c写99乘法表

    int main(int argc,char **argv){ int a; for(a=1;a<=9;a++){ int b; for(b=1;b<=a;b++){ printf(&qu ...

  10. Android RSA加密对象数据

    前几天说了手头项目用MD5和双向加密DES来进行加密的,因为产品是在不断的改造中完善的.考虑到DES和MD5加解密还是会存下一定的风险,于是产品一拍,不行,听你们说MD5加密是不安全的,咱们产品以后做 ...