框架基础:关于ajax设计方案(三)---集成ajax上传技术
之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询。这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的level2的改进就不介绍了,不清楚的可到前几篇博客去看看。我们直接切入主题。
概念介绍:
1. js的FormData:js中在新的版本中已经支持了FormData对象,可以初始化一个空的form,或者初始化已经存在的form,浏览器测试代码。
  
2. 浏览器的支持:浏览器已支持input=file的时候查看文件,具体包括文件的大小(size)和类型(type),浏览器测试如下

  3. xmlhttprequest:支持发送(send方法)新的数据类型,包括DOMString、Document、FormData、Blob、File、ArrayBuffer。具体参见ajax设计方案的规范
工具准备:
1. 前端代码
2. nginx服务器(分离)
3. IIS服务器(部署后台)
4. 后台代码(webAPI)
什么不多说,先贴代码:
前端代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |     /*    *   ajax上传文件    *       url                 文件上传地址    *       fileSelector        input=file 选择器(支持多文件上传,只要后台接口支持)    *       size                文件限制大小    *       fileType            文件限制类型 mime类型    *       success             上传成功处理    *       error               上传失败处理    *       timeout             超时处理    *    *   return: status:  0      请选择文件    *                    1      超出文件限制大小    *                    2      非允许文件格式    * */    upload:function(url,fileSelector,size,fileType,success,error,timeout){        varformdata = newFormData(),fileNode = document.querySelector(fileSelector),fileCount = fileNode.files.length,data={},result ={};        //以下为上传文件限制检查        if( fileCount > 0 ){            tool.each(Array.prototype.slice.call(fileNode.files),function(value){                //检查文件大小                if(value.size > size){                    result["status"] = 1;                    result["errMsg"] = "超出文件限制大小";                }else{                    //检查文件格式.因为支持formdata,自然支持数组的indexof(h5)                    if(fileType.indexOf(value.type)=== -1 ){                        result["status"] = 2;                        result["errMsg"] = "非允许文件格式";                    }else{                        formdata.append(value.name,value);                    };                };            });        }else{            result["status"] = 0;            result["errMsg"] = "请选择文件";        };        if(result.status !== undefined)  returnresult;   //如果有错误信息直接抛出去,结束运行        varajaxParam ={            type:"post",            url:url,            data:formdata,            isFormData:true,            success:success,            error:error,            timeout:timeout        };        ajax.common(ajaxParam);    },}; | 
后端接口代码(C#的webAPI),代码比较简陋,能完成测试就好




    [Route("upload3")]
        public async Task<HttpResponseMessage> PostFormData()
        {
            // Check if the request contains multipart/form-data.
            // 检查该请求是否含有multipart/form-data
            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
            string root = HttpContext.Current.Server.MapPath("~/uploadfile");
            var provider = new ReNameMultipartFormDataStreamProvider(root);
            try
            {
                // Read the form data.
                // 读取表单数据
                var task = await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
                {
                    if (t.IsFaulted || t.IsCanceled)
                    {
                        Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
                    }
                    string fileName = string.Empty;
                    foreach (MultipartFileData file in provider.FileData)
                    {
                        fileName = file.LocalFileName;
                    }
                    //返回上传后的文件全路径
                    return new HttpResponseMessage() { Content = new StringContent(fileName) };
                });
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (System.Exception e)
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
            }
        }
        ///
        /// 重命名上传的文件
        ///
        public class ReNameMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
        {
            public ReNameMultipartFormDataStreamProvider(string root)
                : base(root)
            { }
            public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
            {
                //截取文件扩展名
                string exp = Path.GetExtension(headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"'));
                string name = base.GetLocalFileName(headers);
                return name + exp;
            }
        }




浏览器测试结果(几乎所有主流浏览器都支持,除了IE10以下)
测试代码如下:
| 1 2 3 4 5 6 7 8 9 | vartemp = ajax.upload("/api/ajaxUpload/upload3/", "#file1", 1024 * 1024 * 1, ["image/png","image/bmp"], function(data) {    if(data == "true") {        alert("上传成功!");    }    if(data == "error") {        alert(data);    }});console.log(temp); | 
格式限制测试结果如下
1. 选取超过限制大小的文件

2. 选取非允许格式的文件

3. 未选择上传文件

文件上传成功测试
IE10、11


safari

火狐

谷歌

opera

edge

360浏览器

下面扯一下遇到的问题
问题一 IE8-9兼容问题
在兼容性网站(http://caniuse.com/)查询兼容性,ajax leve2支持IE10+版本,所以如果在IE8-9上使用纯前端代码进行上传文件的话,只有传统的form标签

html代码如下:
| 1 2 3 4 5 6 | <form id="formUpload"action="/api/ajaxUpload/upload2/"method="post"enctype="multipart/form-data"target="framFile">    <input name="isIE8"type="text"value="1"readonly style="display: none"/>    <input id="iefile"type="file"name="age"/>    <input type="submit"value="submit"></form><iframe id="framFile"name="framFile"src="postMsg.html"></iframe> | 
缺点:
1. 每次form提交的时候都会刷新页面,如果想做异步无刷新,用iframe做提交页面
2. IE8-9无法在前端对文件进行大小和类型检查(使用IE的文件组件不安全,因为可以修改系统上所有文件,容易被攻击,而且浏览器都是默认关闭的)
3. 上传文件接口不能有返回值,否则在IE8下会将接口返回值作为文件下载下来,且无法取得返回值(用了N种方法),但是在其他浏览器中ajax的成功事件会去做判断,测试图片如下

一些建议:如果真的要做IE8-9,现在普遍的方案是将flash插件和ajax Level2的上传进行组合,支持H5的用ajax上传,不支持初始化flash上传插件。
PS:对于那些偏执的,一定要在IE8-9用纯前端代码支持上传的,还有一种折中的方案,和这种思想类似,但是我做了优化,思路如下:
需要2个接口:上传文件接口,IE8-9下上传结果查询接口
a. 首先使用form的无刷新上传(ifarme)
b. 后台接收到formdata数据判断是否是IE8-9的上传,是的话将该用户上传文件是否成功的状态改变(不管存库或者其他地方),否则直接返回上传结果
c. 前端在form的submit之后,发起得到一次结果的轮询,如果得到结果,则直接结束轮询,结果查询接口也将该用户的上传文件状态清空
问题二 一般ajax请求和formdata请求,后台取值问题。
传统http请求,可以直接在接口参数中取得数据,但是使用formdata进行ajax请求的话,后台接口需要从formdata对象中取数据,包括文件啥的。因为这个我写后台接口的时候就懵逼了好长一段时间,然后左查查右查查,终于明白取值方式也不一样了。
问题三 关于formdata上传文件,具体能上传多大文件的限制问题
上传文件的限制取决于web容器可接受上传文件的大小,tomcat、IIS等web容器都有自己的设置方法,具体可搜索引擎,你懂的
问题四 前端对大文件的传输解决方案,具体可参考该文章
在新的版本中,就是支持H5的版本中,有了File对象可以切割文件,因为在取到input=file中取到的文件都是File类型,File对象有个方法slice,可以切割文件,然后分配一个xhr上传。主要是后台的切割文件重组问题不是很清楚,所以我暂时也没有集成大文件上传方法。
代码已集成github:https://github.com/GerryIsWarrior/ajax 点颗星星是我最大的鼓励,有什么问题可以博客、邮箱、github上留言
这一次上传版本,代码做过变动,变动如下:
- 增加FormData数据传输方法(postFormData),如果判断到浏览器不支持FormData,则自动使用默认原始的数据传输
- 新增各种类型判断方法,判断类型
- 更新each方法,判断如果传入参数obj为数组而且浏览器支持h5的新特性直接用数组的forEach方法
我的全栈书签,这次更新整理了国内顶级互联网和著名的一些互联网公司的招聘网站,希望大家找到好工作,^_^
github地址:https://github.com/GerryIsWarrior/MyBookmarks
【转发自http://www.cnblogs.com/GerryOfZhong/p/6274536.html】
框架基础:ajax设计方案(三)---集成ajax上传技术
之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询。这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的level2的改进就不介绍了,不清楚的可到前几篇博客去看看。我们直接切入主题。
概念介绍:
1. js的FormData:js中在新的版本中已经支持了FormData对象,可以初始化一个空的form,或者初始化已经存在的form,浏览器测试代码。
  
2. 浏览器的支持:浏览器已支持input=file的时候查看文件,具体包括文件的大小(size)和类型(type),浏览器测试如下

  3. xmlhttprequest:支持发送(send方法)新的数据类型,包括DOMString、Document、FormData、Blob、File、ArrayBuffer。具体参见ajax设计方案的规范
工具准备:
1. 前端代码
2. nginx服务器(分离)
3. IIS服务器(部署后台)
4. 后台代码(webAPI)
什么不多说,先贴代码:
前端代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |     /*    *   ajax上传文件    *       url                 文件上传地址    *       fileSelector        input=file 选择器(支持多文件上传,只要后台接口支持)    *       size                文件限制大小    *       fileType            文件限制类型 mime类型    *       success             上传成功处理    *       error               上传失败处理    *       timeout             超时处理    *    *   return: status:  0      请选择文件    *                    1      超出文件限制大小    *                    2      非允许文件格式    * */    upload:function(url,fileSelector,size,fileType,success,error,timeout){        varformdata = newFormData(),fileNode = document.querySelector(fileSelector),fileCount = fileNode.files.length,data={},result ={};        //以下为上传文件限制检查        if( fileCount > 0 ){            tool.each(Array.prototype.slice.call(fileNode.files),function(value){                //检查文件大小                if(value.size > size){                    result["status"] = 1;                    result["errMsg"] = "超出文件限制大小";                }else{                    //检查文件格式.因为支持formdata,自然支持数组的indexof(h5)                    if(fileType.indexOf(value.type)=== -1 ){                        result["status"] = 2;                        result["errMsg"] = "非允许文件格式";                    }else{                        formdata.append(value.name,value);                    };                };            });        }else{            result["status"] = 0;            result["errMsg"] = "请选择文件";        };        if(result.status !== undefined)  returnresult;   //如果有错误信息直接抛出去,结束运行        varajaxParam ={            type:"post",            url:url,            data:formdata,            isFormData:true,            success:success,            error:error,            timeout:timeout        };        ajax.common(ajaxParam);    },}; | 
后端接口代码(C#的webAPI),代码比较简陋,能完成测试就好




    [Route("upload3")]
        public async Task<HttpResponseMessage> PostFormData()
        {
            // Check if the request contains multipart/form-data.
            // 检查该请求是否含有multipart/form-data
            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
            string root = HttpContext.Current.Server.MapPath("~/uploadfile");
            var provider = new ReNameMultipartFormDataStreamProvider(root);
            try
            {
                // Read the form data.
                // 读取表单数据
                var task = await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
                {
                    if (t.IsFaulted || t.IsCanceled)
                    {
                        Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
                    }
                    string fileName = string.Empty;
                    foreach (MultipartFileData file in provider.FileData)
                    {
                        fileName = file.LocalFileName;
                    }
                    //返回上传后的文件全路径
                    return new HttpResponseMessage() { Content = new StringContent(fileName) };
                });
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (System.Exception e)
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
            }
        }
        ///
        /// 重命名上传的文件
        ///
        public class ReNameMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
        {
            public ReNameMultipartFormDataStreamProvider(string root)
                : base(root)
            { }
            public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
            {
                //截取文件扩展名
                string exp = Path.GetExtension(headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"'));
                string name = base.GetLocalFileName(headers);
                return name + exp;
            }
        }




浏览器测试结果(几乎所有主流浏览器都支持,除了IE10以下)
测试代码如下:
| 1 2 3 4 5 6 7 8 9 | vartemp = ajax.upload("/api/ajaxUpload/upload3/", "#file1", 1024 * 1024 * 1, ["image/png","image/bmp"], function(data) {    if(data == "true") {        alert("上传成功!");    }    if(data == "error") {        alert(data);    }});console.log(temp); | 
格式限制测试结果如下
1. 选取超过限制大小的文件

2. 选取非允许格式的文件

3. 未选择上传文件

文件上传成功测试
IE10、11


safari

火狐

谷歌

opera

edge

360浏览器

下面扯一下遇到的问题
问题一 IE8-9兼容问题
在兼容性网站(http://caniuse.com/)查询兼容性,ajax leve2支持IE10+版本,所以如果在IE8-9上使用纯前端代码进行上传文件的话,只有传统的form标签

html代码如下:
| 1 2 3 4 5 6 | <form id="formUpload"action="/api/ajaxUpload/upload2/"method="post"enctype="multipart/form-data"target="framFile">    <input name="isIE8"type="text"value="1"readonly style="display: none"/>    <input id="iefile"type="file"name="age"/>    <input type="submit"value="submit"></form><iframe id="framFile"name="framFile"src="postMsg.html"></iframe> | 
缺点:
1. 每次form提交的时候都会刷新页面,如果想做异步无刷新,用iframe做提交页面
2. IE8-9无法在前端对文件进行大小和类型检查(使用IE的文件组件不安全,因为可以修改系统上所有文件,容易被攻击,而且浏览器都是默认关闭的)
3. 上传文件接口不能有返回值,否则在IE8下会将接口返回值作为文件下载下来,且无法取得返回值(用了N种方法),但是在其他浏览器中ajax的成功事件会去做判断,测试图片如下

一些建议:如果真的要做IE8-9,现在普遍的方案是将flash插件和ajax Level2的上传进行组合,支持H5的用ajax上传,不支持初始化flash上传插件。
PS:对于那些偏执的,一定要在IE8-9用纯前端代码支持上传的,还有一种折中的方案,和这种思想类似,但是我做了优化,思路如下:
需要2个接口:上传文件接口,IE8-9下上传结果查询接口
a. 首先使用form的无刷新上传(ifarme)
b. 后台接收到formdata数据判断是否是IE8-9的上传,是的话将该用户上传文件是否成功的状态改变(不管存库或者其他地方),否则直接返回上传结果
c. 前端在form的submit之后,发起得到一次结果的轮询,如果得到结果,则直接结束轮询,结果查询接口也将该用户的上传文件状态清空
问题二 一般ajax请求和formdata请求,后台取值问题。
传统http请求,可以直接在接口参数中取得数据,但是使用formdata进行ajax请求的话,后台接口需要从formdata对象中取数据,包括文件啥的。因为这个我写后台接口的时候就懵逼了好长一段时间,然后左查查右查查,终于明白取值方式也不一样了。
问题三 关于formdata上传文件,具体能上传多大文件的限制问题
上传文件的限制取决于web容器可接受上传文件的大小,tomcat、IIS等web容器都有自己的设置方法,具体可搜索引擎,你懂的
问题四 前端对大文件的传输解决方案,具体可参考该文章
在新的版本中,就是支持H5的版本中,有了File对象可以切割文件,因为在取到input=file中取到的文件都是File类型,File对象有个方法slice,可以切割文件,然后分配一个xhr上传。主要是后台的切割文件重组问题不是很清楚,所以我暂时也没有集成大文件上传方法。
代码已集成github:https://github.com/GerryIsWarrior/ajax 点颗星星是我最大的鼓励,有什么问题可以博客、邮箱、github上留言
这一次上传版本,代码做过变动,变动如下:
- 增加FormData数据传输方法(postFormData),如果判断到浏览器不支持FormData,则自动使用默认原始的数据传输
- 新增各种类型判断方法,判断类型
- 更新each方法,判断如果传入参数obj为数组而且浏览器支持h5的新特性直接用数组的forEach方法
我的全栈书签,这次更新整理了国内顶级互联网和著名的一些互联网公司的招聘网站,希望大家找到好工作,^_^
github地址:https://github.com/GerryIsWarrior/MyBookmarks
【转发自http://www.cnblogs.com/GerryOfZhong/p/6274536.html】
框架基础:关于ajax设计方案(三)---集成ajax上传技术的更多相关文章
- 框架基础:ajax设计方案(三)---集成ajax上传技术
		之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询.这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的l ... 
- 前端通信:ajax设计方案(三)--- 集成ajax上传技术
		在此之前让我感慨一下现在的前端开发的氛围.我遇到好多人,给我的观念都是,这个东西这个框架有了,那个东西那个框架做了,前端嘛,学几个框架,这个拼凑一下那个拼凑一下就好了.其实我想问,东西都框架做了,那你 ... 
- Ajax的原理及Django上传组件
		title: Ajax的原理及Django上传组件 tags: Django --- Ajax的原理及Django上传组件 Ajax的原理 ajax 是异步JavaScript和xml ajax就是向 ... 
- ajax方式提交带文件上传的表单,上传后不跳转
		ajax方式提交带文件上传的表单 一般的表单都是通过ajax方式提交,所以碰到带文件上传的表单就比较麻烦.基本原理就是在页面增加一个隐藏iframe,然后通过ajax提交除文件之外的表单数据,在表单数 ... 
- Ajax+PHP实现异步图片上传
		1.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ... 
- Django学习——ajax发送其他请求、上传文件(ajax和form两种方式)、ajax上传json格式、 Django内置序列化(了解)、分页器的使用
		1 ajax发送其他请求 1 写在form表单 submit和button会触发提交 <form action=""> </form> 注释 2 使用inp ... 
- 使用.NET框架、Web service实现Android的文件上传(二)
		aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYUAAAKpCAIAAADcx6fPAAAgAElEQVR4nOydd1hT5+LHg1attbfr1t ... 
- 框架基础:ajax设计方案(三)--- 集成ajax上传技术  大文件/超大文件前端切割上传,后端进行重组
		马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ... 
- 前端通信:ajax设计方案(四)--- 集成ajax上传技术  大文件/超大文件前端切割上传,后端进行重组
		马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ... 
随机推荐
- Oracle 物理和逻辑备库健康监測的一个根据
			以以下keyword眼为例: 1 物理备库健康检查根据: Tue Apr 22 16:44:51 CST 2014Media Recovery Log /data/CMS/arch_log/1_583 ... 
- 【转载】lvs为何不能完全替代DNS轮询
			上一篇文章"一分钟了解负载均衡的一切"引起了不少同学的关注,评论中大家争论的比较多的一个技术点是接入层负载均衡技术,部分同学持这样的观点: 1)nginx前端加入lvs和keepa ... 
- Vue 基础
			1. data 数据 methods 方法 watch 监听变化 2. 模版指令(类似 angular) {{}} v-text 渲染数据 v-html html 结构 3. v-if v-show ... 
- Spring Boot与Micronaut性能比较
			文章转载出处:微信公众号——锅外的大佬 链接:https://mp.weixin.qq.com/s/MdBByJ0ju-rROKg7jsWygA 今天我们将比较两个在JVM上构建微服务的框架:Spri ... 
- java:[1,0] illegal character: \65279 问题
			部署项目的时候报下面错误 [java] view plaincopyprint? java:[1,0] illegal character: \65279 java:[1,10] class, int ... 
- ASP.NET MVC判断基于Cookie的Session过期
			当我们第一次请求访问时,可以看到Response的Set-Cookie里添加了ASP.NET_SessionId的值,以后再访问时可以看到Resquest里的Cookie已经包含这个Key. Se ... 
- HTML CSS 编码规范
			返璞归真,代码规范也是一门艺术 黄金定律 永远遵循同一套编码规范 -- 可以是这里列出的,也可以是你自己总结的.如果你发现本规范中有任何错误,敬请指正.通过open an issue on GitHu ... 
- 包、修饰符、内部类、匿名内部类(java基础知识十)
			1.package关键字的概述及作用 * A:为什么要有包 * 将字节码(.class)进行分类存放 * B:包的概述 * * C:包的作用 * 包名要定义在第一行, ... 
- hdu 1286 找新朋友(欧拉函数)
			题意:欧拉函数 思路:欧拉函数 模板,代码略. 
- sql注入原理与实践
			转自:http://blog.csdn.net/stilling2006/article/details/8526458 1.1.1 摘要 日前,国内最大的程序员社区CSDN网站的用户数据库被黑客公开 ... 
