之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询。这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的level2的改进就不介绍了,不清楚的可到前几篇博客去看看。我们直接切入主题。

概念介绍:

  1. js的FormData:js中在新的版本中已经支持了FormData对象,可以初始化一个空的form,或者初始化已经存在的form,浏览器测试代码。

  

  2. 浏览器的支持:浏览器已支持input=file的时候查看文件,具体包括文件的大小(size)和类型(type),浏览器测试如下

  3. xmlhttprequest:支持发送(send方法)新的数据类型,包括DOMStringDocumentFormDataBlobFileArrayBuffer。具体参见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){
        var formdata = new FormData(),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)  return result;   //如果有错误信息直接抛出去,结束运行
 
        var ajaxParam ={
            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
var temp = 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上留言

这一次上传版本,代码做过变动,变动如下:

  1. 增加FormData数据传输方法(postFormData),如果判断到浏览器不支持FormData,则自动使用默认原始的数据传输
  2. 新增各种类型判断方法,判断类型
  3. 更新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方法)新的数据类型,包括DOMStringDocumentFormDataBlobFileArrayBuffer。具体参见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){
        var formdata = new FormData(),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)  return result;   //如果有错误信息直接抛出去,结束运行
 
        var ajaxParam ={
            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
var temp = 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上留言

这一次上传版本,代码做过变动,变动如下:

  1. 增加FormData数据传输方法(postFormData),如果判断到浏览器不支持FormData,则自动使用默认原始的数据传输
  2. 新增各种类型判断方法,判断类型
  3. 更新each方法,判断如果传入参数obj为数组而且浏览器支持h5的新特性直接用数组的forEach方法

我的全栈书签,这次更新整理了国内顶级互联网和著名的一些互联网公司的招聘网站,希望大家找到好工作,^_^

github地址:https://github.com/GerryIsWarrior/MyBookmarks

【转发自http://www.cnblogs.com/GerryOfZhong/p/6274536.html】

框架基础:关于ajax设计方案(三)---集成ajax上传技术的更多相关文章

  1. 框架基础:ajax设计方案(三)---集成ajax上传技术

    之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询.这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的l ...

  2. 前端通信:ajax设计方案(三)--- 集成ajax上传技术

    在此之前让我感慨一下现在的前端开发的氛围.我遇到好多人,给我的观念都是,这个东西这个框架有了,那个东西那个框架做了,前端嘛,学几个框架,这个拼凑一下那个拼凑一下就好了.其实我想问,东西都框架做了,那你 ...

  3. Ajax的原理及Django上传组件

    title: Ajax的原理及Django上传组件 tags: Django --- Ajax的原理及Django上传组件 Ajax的原理 ajax 是异步JavaScript和xml ajax就是向 ...

  4. ajax方式提交带文件上传的表单,上传后不跳转

    ajax方式提交带文件上传的表单 一般的表单都是通过ajax方式提交,所以碰到带文件上传的表单就比较麻烦.基本原理就是在页面增加一个隐藏iframe,然后通过ajax提交除文件之外的表单数据,在表单数 ...

  5. Ajax+PHP实现异步图片上传

    1.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...

  6. Django学习——ajax发送其他请求、上传文件(ajax和form两种方式)、ajax上传json格式、 Django内置序列化(了解)、分页器的使用

    1 ajax发送其他请求 1 写在form表单 submit和button会触发提交 <form action=""> </form> 注释 2 使用inp ...

  7. 使用.NET框架、Web service实现Android的文件上传(二)

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYUAAAKpCAIAAADcx6fPAAAgAElEQVR4nOydd1hT5+LHg1attbfr1t ...

  8. 框架基础:ajax设计方案(三)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

    马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...

  9. 前端通信:ajax设计方案(四)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

    马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...

随机推荐

  1. ALERT日志中常见监听相关报错之三:ORA-609 TNS-12537 and TNS-12547 or TNS-12170 TNS-12535错误的排查

    1.11G中ALERT日志中有报错ORA-609 TNS-12537 and TNS-12547 or TNS-12170  12170, 'TNS-12535等问题的解决方法: Troublesho ...

  2. memchached你知道和不知道的事

  3. 获取DOM父元素和子元素

    利用javascript可以遍历DOM树,这篇文章介绍用获取一个DOM元素的所有父节点和获取一个DOM元素的所以子孙节点. 1.获取所有父节点.用递归的方法,用parentNode属性. <!D ...

  4. linux进程间通信消息队列:msgsnd: Invalid argument

    今天写了个消息队列的小测试程序结果send端程序总是出现:msgsnd: Invalid argument,搞了半个小时也没搞明白,后来查资料发现我将(st_msg_buf.msg_type = 0; ...

  5. 【bzoj2761】【JLOI2011】【不反复数字】【平衡树】

    Description 给出N个数,要求把当中反复的去掉.仅仅保留第一次出现的数. 比如,给出的数为1 2 18 3 3 19 2 3 6 5 4.当中2和3有反复.去除后的结果为1 2 18 3 1 ...

  6. ECMAScript学习笔记

    1. ECMAScript不存在块级作用域,因此在循环内部定义的变量,在循环外也是可以访问的 eg: var count =10; fpr(var i=0; i<count; i++){ ale ...

  7. js中的连等==和全等===

    ===是没有强制类型转换的,和其他大部分语言的==是一样的.而js中==是有类型转换的. 比如说"true"==true就是错的,Boolean("false" ...

  8. POJ3414 Pots —— BFS + 模拟

    题目链接:http://poj.org/problem?id=3414 Pots Time Limit: 1000MS   Memory Limit: 65536K Total Submissions ...

  9. linux初级学习笔记九:linux I/O管理,重定向及管道!(视频序号:04_3)

    本节学习的命令:tr,tee,wc 本节学习的技能: 计算机的组成 I/O管理及重定向     管道的使用 知识点九:管理及IO重定向(4_3) 计算机组成: 运算器.控制器: CPU 存储器:RAM ...

  10. html5--6-59 其他常用CSS属性

    html5--6-59 其他常用CSS属性 实例 学习要点 了解opacity属性:透明度设定 了解cursor属性:自定义鼠标样式 了解CSS新单位rem和em的区别 了解轮廓outline的设置 ...