这一个月来一直在做项目,在做项目的时候遇到了不少的问题和坑,归纳了一下,主要是文件上传、停止 Promise 链式调用和 chrome 自动填充,整理总结了一下解决方法。
在选择文件之前,我们需要对文件类型进行一些过滤的操作。
通过 input:file 来选择我们需要的文件类型,有两个属性值是我们需要的:
- 后缀名 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 设置为
来过滤所有非图片的文件。虽然这种方式简单粗暴,但是在新版本的 chrome 中,会出现点击 input 之后,文件选择框弹出非常慢的问题。将
- image/*
改为指定的图片格式,比如指定几种常用格式,就能解决这个问题。
- accept="image/*"
- <inputtype="file"name="file"accept="image/jpg,image/jpeg,image/png,image/gif">
在 input 选取文件后,我们可以监听 chang 事件来获取所选取的文件。
- $('input').on('change',function(){
- if(this.files && this.files.length>0){
- console.log(this.files);
- }
- });
这里获取到的 this.files 是一个 FileList 对象,也是一个类数组对象,可以通过 this.files[index] 来获取每一个文件。
这个数组对象中的每个对象有以下几个属性:
在获取到文件后,我们可以根据 type 和 size 对文件的类型和大小进行过滤匹配。匹配后需要对文件的内容进行读取。HTML5 定义了一个 FileReader 对象用来读取文件。FileReader 使用方式也非常简单,需要创建 FileReader 对象并调用方法。
- 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
的值为 null,否则即是读取的结果,绝大多数的程序都会在成功读取文件的时候,抓取这个值。
- result
- var fileReader = new FileReader();
- fileReader.readAsDataURL(imgFile);
- reader.onload = function(event) {
- console.log(this.result);
- }
通过 readAsDataURL 来读取图片变成 DataUrl 格式。将其字符串嵌入到页面中,我们可以看到读取后的图片,通过这种方式实现选取图片后的预览效果。
- <imgsrc="data:image/gif;base64,R0lGODlhMwAxAIAAAAAAAP///
- yH5BAAAAAAALAAAAAAzADEAAAK8jI+pBr0PowytzotTtbm/DTqQ6C3hGX
- ElcraA9jIr66ozVpM3nseUvYP1UEHF0FUUHkNJxhLZfEJNvol06tzwrgd
- LbXsFZYmSMPnHLB+zNJFbq15+SOf50+6rG7lKOjwV1ibGdhHYRVYVJ9Wn
- k2HWtLdIWMSH9lfyODZoZTb4xdnpxQSEF9oyOWIqp6gaI9pI1Qo7BijbF
- ZkoaAtEeiiLeKn72xM7vMZofJy8zJys2UxsCT3kO229LH1tXAAAOw==">
在通过 readAsDataURL 方法读取到文件的 DataUrl 后,我们可以将这么长的字符串直接放到 ajax 中作为 string 类型发送到后台解析(建议使用 post 方式)。不过会有一定的局限性,就是如果文件很大,服务器可能会拒绝接受这么长的字符串。
FormData 对象是 HTML5 新增的一个对象,目前一些主流的浏览器都已经兼容了,我们可以通过 FormData 来向服务器传递数据。
- 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 数据来发送,浏览器会报一个非法调用的错误。
在发送 FormData 对象时,还需要给 ajax 加上另外两个属性:
- $.ajax({
- url:'this is your url',
- type:'get',
- data:formData,
- processData:false,
- contentType:false,
- success: function(data){
- console.log(data);
- }
- });
jQuery 在发送异步请求的时候会自动将 data 数据进行序列化处理,转化成 key/value 格式的字符串,加上
说明禁止对数据进行序列化处理。contentType 用来指定发送至服务器时的内容编码类型。
- processData:false
如果每个表单数据都需要使用 append 来添加就比较麻烦,FormData 还支持直接从 html 中的表单生成数据,就是在 html 页面中已经有数据了,然后 FormData 可以直接把这个表单的数据写入这个对象,然后直接提交给后台。
- <formid="form">
- <inputtype="file"name="img"accept="image/jpeg,image/jpg,image/png,image/gif">
- <inputtype="text"name="username"/>
- <inputtype="button"class="submit"value="submit"/>
- </form>
我们定义了一个 form 表单,有 text 类型和 file 类型的 input。
- $('.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 的链式调用虽然方便我们不用再写恶心的嵌套回调,但是有一个问题,就是如果第一个异步没有发送成功,进入了 reject 函数,后面的链式调用的 resolve 函数的 data 都是 undefined,对后面的 then 调用造成了很大的问题。
在每个 then 方法中对 data 进行非空判断。
如果当前的 Promise 进入 reject 函数,对后面的 Promise 都进行 abort 操作,该方法适用于 jQuery 的 Promise 操作。
- 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 会莫名其妙的抽风,在我们不想要填充的地方自动给 input 填充上账号,为了不让 chrome 自动填充,我们采用下面的方式禁止自动填充:
- <inputtype="text"style="display:none"/>
- <inputtype="password"id="pwd"autocomplete="off"/>
我们在我们需要的 input#pwd 上面加一个 display:none 隐藏的 div,然后给 input#pwd 加上一个 autocomplete="off" 的属性,这样,这个 input#pwd 就不会自动填充了。
来源: http://www.tuicool.com/articles/zumyYfv