0202年了, jQuery可以继续梭哈…
记录一下jQuery使用ajax上传文件和Post表单的操作
相关文档: jQuery官方ajax文档
上传文件
仅上传单个文件
前端jquery使用
FormData
构造请求数据
<!-- 防止点击提交按钮页面自动刷新, 所以设置 onsubmit=\"return false;\" --><form onsubmit=\"return false;\" method=\"post\" class=\"form-group\"><div><input type=\"file\" id=\"i-avatar\" name=\"image\" /></div><div class=\"mt-2\"><button class=\"btn btn-primary\" onclick=\"uploadImage()\">提交</button></div></form><script type=\"text/javascript\">function uploadImage() {const form = new FormData();form.append(\"file\", $(\"#i-avatar\").prop(\"files\")[0]);$.ajax({type: \"post\",url: \"/videos/image\",contentType: false,data: form,processData: false,success: function () {window.alert(\"success\");}});}</script>
后端Action:
[HttpPost(\"image\")]public async Task<IActionResult> UploadImage([FromForm] IFormFile file){using (var rs = file.OpenReadStream()){string path = Path.Combine(_webHostEnvironment.WebRootPath, \"upload\", \"images\", file.FileName);Directory.CreateDirectory(Path.GetDirectoryName(path));using (var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite)){await rs.CopyToAsync(fs);await fs.FlushAsync();}}return Ok();}
这里就是拿到文件流然后保存到
~/wwwroot/upload/image
文件夹下, 这里文件名直接使用了用户上传的文件名, 微软官方文档中不建议这么做, 因为可能会有安全问题
写好之后在浏览器中测试, 看一下最终发出的请求是什么样的
可以看到, 虽然我们再ajax函数中将contentType设置成了false, 但是浏览器自动为我们添上了
content-type: multipart/form-data; boundary=----WebKitFormBoundaryxZyRmmXUfCxJRX7A
, 一开始我手动设置了
contentType: multipart/form-data
, 这样肯定是不行的, 至于为什么, 因为没有设置
boundary
这个东西啊
简单总结一下ajax要注意的配置
dataType
, 表示期望服务器返回的类型, 这个不能乱填, 如果返回的格式是
json
则设置为
json
, 返回的是纯文字, 则设置为
text
, 这个属性还可以被设置为
xml
,
html
,
script
,
jsonp
, 如果不设置这个属性, 那么jQuery会根据响应的MIME来自动推断, 如果你不确定响应的格式, 那么就不要设置这个属性;
contentType
, 发送数据给服务器时, 指定的数据的格式, 注意, 一定要注意, 这个属性是有默认值的, 且默认值是
application/x-www-form-urlencoded; ciharset=UTF-8
, 如果服务器不接受这种类型的输入, 那么就会请求失败, 所以务必确认好再设置, 如果将这个属性设置为
false
, 那么jquery就不会在请求头的加入
content-type
processData
, 告诉jquery, 是否要将请求数据内容转换成
application/x-www-form-urlencoded
, 默认是
true
, 也就是说jquery会将ajax的data属性格式化成
application/x-www-form-urlencoded
这种数据格式, 关于Post请求的数据格式的问题, 可以看这篇博文, HTTP_POST请求的数据格式, 非常详细, 如果是上传文件的场景, 要将这个属性设置成
false
上传文件的同时, 再传一些其他 Key-Value 类型数据
在用户注册的场景下, 需要提交用户名, 密码和头像等信息, 头像往往是一个图片文件; 当然, 也可以先单独上传头像, 然后传图片的url给用户注册接口api, 但是, 我觉得更好的做法是直接把文件一起传给用户注册接口的api, 这个接口同时保存用户头像;
还是使用
FormData
对象来构造请求数据:
<form onsubmit=\"return false;\"><div><label>用户名: <inpu56ct type=\"text\" id=\"username\" name=\"username\" value=\"\" /></label></div><div><label>密码: <input type=\"password\" id=\"password\" name=\"password\" value=\"\" /></label></div><div><label>头像: <input type=\"file\" id=\"avatar\" name=\"avatar\" value=\"\" multiple/></label></div><div><button class=\"btn btn-primary\" onclick=\"registerUser()\">注册</button></div><div id=\"register-result\"></div></form><script type=\"text/javascript\">function registerUser() {const formData = new FormData();formData.set(\"username\", $(\"#username\").val());formData.set(\"password\", $(\"#password\").val());const files = $(\"#avatar\").prop(\"files\");if (files && files.length > 0)formData.set(\"avatar\", files[0]);// 如果有多个文件怎么办?//for (let i = 0; i < files.length; i++) {// formData.set(i, files[i]);//}$.ajax({url: \"/user\",type: \"post\",processData: false,contentType: false,data: formData,success: function (result) {$(\"#register-result\").56ctext(JSON.stringify(result));}});}</script>
后台接收数据:
[HttpPost]public Task<IActionResult> Register([FromForm] IFormCollection form){return Task.FromResult<IActionResult>(Ok(form));}
测试看一下请求是什么样的
可以看到
content-type
依旧是
multipart/formdata; boundary...
这种, 但是, 如果我要在传图片的同时, 传一些其他数据数据或者复杂对象怎么办啊? 比如我后台定义这样的对象:
public class VideoTagDto{public string TagName { get; set; }public DateTime CreateDate { get; set; }}public class ActorDto{public string Name { get; set; }public string Sex { get; set; }public string Country { get; set; }public DateTime Birth { get; set; }}public class VideoDto{public string Name { get; set; }public DateTime PublishDate { get; set; }public IList<VideoTagDto> VideoTags { get; set; }public IList<ActorDto> Actors { get; set; }public IFormFile CoverImage { get; set; }public VideoDto(){VideoTags = nead1w List<VideoTagDto>();Actors = new List<ActorDto>();}}
前台有没有办法可以让后台接口接收到
VideoDto
呢?
VideoDto
里面包含了
IFormFile
类型
CoverImage
;
不好意思, 好像不太可以, 我尝试了希望使用
application/json
格式来上传文件和其他复杂对象, 不过浏览器的
File
对象没办法序列化成JSON格式; 所以只能先上传文件, 然后拿到文件的url, 再和其他数据一起上传