这一个月来一直在做项目,在做项目的时候遇到了不少的问题和坑,归纳了一下,主要是文件上传、停止Promise链式调用和chrome自动填充,整理总结了一下解决方法。

一、文件上传

选择文件

  在选择文件之前,我们需要对文件类型进行一些过滤的操作。

文件类型

  通过input:file来选择我们需要的文件类型,有两个属性值是我们需要的:

  • accept:表示可以选择的文件MIME类型,多个MIME类型用英文逗号分开,常用的MIME类型见下表。
  • multiple:是否可以选择多个文件。
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
后缀名 MIME名称
*.3gpp audio/3gpp, video/3gpp
*.ac3 audio/ac3
*.asf allpication/vnd.ms-asf
*.au audio/basic
*.css text/css
*.csv text/csv
*.doc application/msword
*.dot application/msword
*.dtd application/xml-dtd
*.dwg image/vnd.dwg
*.dxf image/vnd.dxf
*.gif image/gif
*.htm text/html
*.html text/html
*.jp2 image/jp2
*.jpe image/jpeg
*.jpeg image/jpeg
*.jpg image/jpeg
*.js text/javascript, application/javascript
*.json application/json
*.mp2 audio/mpeg, video/mpeg
*.mp3 audio/mpeg
*.mp4 audio/mp4, video/mp4
*.mpeg video/mpeg
*.mpg video/mpeg
*.mpp application/vnd.ms-project
*.ogg application/ogg, audio/ogg
*.pdf application/pdf
*.png image/png
*.pot application/vnd.ms-powerpoint
*.pps application/vnd.ms-powerpoint
*.ppt application/vnd.ms-powerpoint
*.rtf application/rtf, text/rtf
*.svf image/vnd.svf
*.tif image/tiff
*.tiff image/tiff
*.txt text/plain
*.wdb application/vnd.ms-works
*.wps application/vnd.ms-works
*.xhtml application/xhtml+xml
*.xlc application/vnd.ms-excel
*.xlm application/vnd.ms-excel
*.xls application/vnd.ms-excel
*.xlt application/vnd.ms-excel
*.xlw application/vnd.ms-excel
*.xml text/xml, application/xml
*.zip aplication/zip
*.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

  但是在开发时,我们习惯把accept设置为image/*来过滤所有非图片的文件。虽然这种方式简单粗暴,但是在新版本的chrome中,会出现点击input之后,文件选择框弹出非常慢的问题。将accept="image/*"改为指定的图片格式,比如指定几种常用格式,就能解决这个问题。

1
<input type="file" name="file" accept="image/jpg,image/jpeg,image/png,image/gif">

文件读取

  在input选取文件后,我们可以监听chang事件来获取所选取的文件。

1
2
3
4
5
$('input').on('change',function(){
if(this.files && this.files.length>0){
console.log(this.files);
}
});

  这里获取到的this.files是一个FileList对象,也是一个类数组对象,可以通过this.files[index]来获取每一个文件。

select-files

  这个数组对象中的每个对象有以下几个属性:

  • lastModified:上次修改时间的时间戳
  • lastModifiedDate:上次修改时间的日期
  • name:文件名称
  • size:文件大小
  • type:文件类型
  • webkitRelativePath:

  在获取到文件后,我们可以根据type和size对文件的类型和大小进行过滤匹配。匹配后需要对文件的内容进行读取。HTML5定义了一个FileReader对象用来读取文件。FileReader使用方式也非常简单,需要创建FileReader对象并调用方法。

1
2
3
if(window.FileReader){
var fileReader = new FileReader();
}

  FileReader的实例对象有4个方法,三个方法是用来读取文件的,还有一个方法用来中断读取。

方法 参数 描述
abort none 中断读取
readAsBinaryString file 将文件读取为二进制码
readAsDataURL file 将文件读取为 DataURL
readAsText file, [encoding] 将文件读取为文本

  readAsText主要用来读取文本文件的内容,readAsDataURL用来读取文件并将其转为DataUrl格式。FileReader还有一系列完整的事件函数,用来捕获读取文件时的状态。

事件 描述
onabort 中断时触发
onerror 出错时触发
onload 文件读取成功完成时触发
onloadend 读取完成触发,无论成功或失败
onloadstart 读取开始时触发
onprogress 读取中

  文件一旦开始读取,无论成功或失败,实例的result属性都会被填充。如果读取失败,则result的值为 null,否则即是读取的结果,绝大多数的程序都会在成功读取文件的时候,抓取这个值。

1
2
3
4
5
var fileReader = new FileReader();
fileReader.readAsDataURL(imgFile);
reader.onload = function(event) {
console.log(this.result);
}

  通过readAsDataURL来读取图片变成DataUrl格式。将其字符串嵌入到页面中,我们可以看到读取后的图片,通过这种方式实现选取图片后的预览效果。

1
2
3
4
5
6
<img src="data:image/gif;base64,R0lGODlhMwAxAIAAAAAAAP///
yH5BAAAAAAALAAAAAAzADEAAAK8jI+pBr0PowytzotTtbm/DTqQ6C3hGX
ElcraA9jIr66ozVpM3nseUvYP1UEHF0FUUHkNJxhLZfEJNvol06tzwrgd
LbXsFZYmSMPnHLB+zNJFbq15+SOf50+6rG7lKOjwV1ibGdhHYRVYVJ9Wn
k2HWtLdIWMSH9lfyODZoZTb4xdnpxQSEF9oyOWIqp6gaI9pI1Qo7BijbF
ZkoaAtEeiiLeKn72xM7vMZofJy8zJys2UxsCT3kO229LH1tXAAAOw==">

base64方式上传

  在通过readAsDataURL方法读取到文件的DataUrl后,我们可以将这么长的字符串直接放到ajax中作为string类型发送到后台解析(建议使用post方式)。不过会有一定的局限性,就是如果文件很大,服务器可能会拒绝接受这么长的字符串。

FormData方式上传

  FormData对象是HTML5新增的一个对象,目前一些主流的浏览器都已经兼容了,我们可以通过FormData来向服务器传递数据。

1
2
3
4
5
6
7
8
9
10
11
var formData = new FormData();
formData.append('username','corner');
formData.append('pwd','corner');
$.ajax({
url:'this is your url',
type:'get',
data:formData,
success: function(data){
console.log(data);
}
});

  但是如果这样直接把FormData对象作为data数据来发送,浏览器会报一个非法调用的错误。
error
  在发送FormData对象时,还需要给ajax加上另外两个属性:

1
2
3
4
5
6
7
8
9
10
$.ajax({
url:'this is your url',
type:'get',
data:formData,
processData:false,
contentType:false,
success: function(data){
console.log(data);
}
});

  jQuery在发送异步请求的时候会自动将data数据进行序列化处理,转化成key/value格式的字符串,加上processData:false说明禁止对数据进行序列化处理。contentType用来指定发送至服务器时的内容编码类型。
  如果每个表单数据都需要使用append来添加就比较麻烦,FormData还支持直接从html中的表单生成数据,就是在html页面中已经有数据了,然后FormData可以直接把这个表单的数据写入这个对象,然后直接提交给后台。

1
2
3
4
5
<form id="form">
<input type="file" name="img" accept="image/jpeg,image/jpg,image/png,image/gif">
<input type="text" name="username"/>
<input type="button" class="submit" value="submit"/>
</form>

  我们定义了一个form表单,有text类型和file类型的input。

1
2
3
4
5
6
7
8
9
10
11
12
$('.submit').on('click',function(){
var formData = new FormData($('#form')[0]);
$.ajax({
url:'this is your url',
type:'post',
data:formData,
processData:false,
contentType:false,
success: function(data){
console.log(data);
}
});

  FormData还支持异步的上传文件,以前我们上传文件,需要写一个表单直接刷新提交,现在可以使用FormData,在构造这个对象的时候,把表单的对象,作为一个参数放进去,就可以了,然后FormData,就会得到这个表单对象里面的所有的参数,甚至我们在表单中,都不需要声明enctype ="multipart/form-data",就可以直接提交。
  使用FormData的优点,第一是在提交表单的时候,不需要写大量的js来获得表单数据,直接把表单对象构造就行了。第二就是可以直接异步上传文件。

二、停止Promise链式调用

  Promise的链式调用虽然方便我们不用再写恶心的嵌套回调,但是有一个问题,就是如果第一个异步没有发送成功,进入了reject函数,后面的链式调用的resolve函数的data都是undefined,对后面的then调用造成了很大的问题。

第一个解决方法

  在每个then方法中对data进行非空判断。

第二个解决方法

  如果当前的Promise进入reject函数,对后面的Promise都进行abort操作,该方法适用于jQuery的Promise操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
getAsync1()
.then(function(data){
return getAsync2();
},function(err){
return getAsync2().abort();
})
.then(function(data){
return getAsync3();
},function(err){
if(err.statusText != 'abort'){
// ...不是通过上一个Promise的abort进入
}
return getAsync3().abort();
});

三、chrome禁止自动填充

  chrome保存密码并且自动填充的功能确实能够方便我们在浏览网站的时候登录进去,但是有时候chrome会莫名其妙的抽风,在我们不想要填充的地方自动给input填充上账号,为了不让chrome自动填充,我们采用下面的方式禁止自动填充:

1
2
<input type="text" style="display:none"/>
<input type="password" id="pwd" autocomplete="off"/>

  我们在我们需要的input#pwd上面加一个display:none隐藏的div,然后给input#pwd加上一个autocomplete=”off”的属性,这样,这个input#pwd就不会自动填充了。

四、文件下载

  刚开始,笔者在页面上用jQuery的$.post方法发送一个请求给服务器,然后服务器根据这个参数再生成相应的一个文件流返回给客户端。但是,在$.post方法的回调函数中,只能处理xml, json, script, or html类型,对返回的文件流却没办法弹出对话框让用户下载了。经过百度,看到了很多人采用隐藏form提交的方式,再用response来推就可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function exportRecord(ajaxData) {
var form = $("<form>");
form.attr('style', 'display:none');
form.attr('target', '');
form.attr('method', ajaxData.method); //请求方式
form.attr('action', ajaxData.url);//请求地址
var input1 = $('<input>');//将你请求的数据模仿成一个input表单
input1.attr('type', 'hidden');
input1.attr('name', '');//该输入框的name
input1.attr('value', '');//该输入框的值value
$('body').append(form);
form.append(input1);
form.submit();
form.remove();
}

  这种方法发出的请求格式类似于username=username&password=password. 代码中的name就是请求中的key,代码中的value就是请求数据中的value

本文地址: http://xieyufei.com/2017/03/29/Project-Summary.html