移动端H5图片上传的那些坑

42

上周做一个关于移动端图片压缩上传的功能。期间踩了几个坑,在此总结下。

大体的思路是,部分API的兼容性请参照caniuse

  1. 利用FileReader,读取blob对象,或者是file对象,将图片转化为data uri的形式。
  2. 使用canvas,在页面上新建一个画布,利用canvas提供的API,将图片画入这个画布当中。
  3. 利用canvas.toDataURL(),进行图片的压缩,得到图片的data uri的值
  4. 上传文件。

步骤1当中,在进行图片压缩前,还是对图片大小做了判断的,如果图片大小大于200KB时,是直接进行图片上传,不进行图片的压缩,如果图片的大小是大于200KB,则是先进行图片的压缩再上传:

    <input type="file" id="choose" accept="image/*">
    var fileChooser = document.getElementById("choose"),
        maxSize = 200 * 1024;   //200KB
    fileChoose.change = function() {
        var file = this.files[0],   //读取文件
            reader = new FileReader();
            
            reader.onload = function() {
                var result = this.result,   //result为data url的形式
                    img = new Image(),
                    img.src = result;
                    
                    
                if(result.length < maxSize) {  
                    imgUpload(result);      //图片直接上传
                } else {
                    var data = compress(img);    //图片首先进行压缩
                    imgUpload(data);                //图片上传
                }
            }
            
            reader.readAsDataURL(file);
    }

步骤2,3:

    var canvas = document.createElement('canvas'),
        ctx = canvas.getContext('2d');
        
    function compress(img) {
        canvas.width = img.width;
        canvas.height = img.height;
        
        //利用canvas进行绘图
        
        //将原来图片的质量压缩到原先的0.2倍。
        var data = canvas.toDataURL('image/jpeg', 0.2); //data url的形式
        
        return data;
    }

在利用canvas进行绘图的过程中,IOS图片上传过程中,存在着这样的问题:

  1. 当你竖着拿手机的时候,拍完照,上传图片时,会出现照片自动旋转的情况,而横着拍照并上传图片时不会出现这个问题。这个时候如果想纠正图片自动旋转的情况,将图片转化为二进制的数据(使用了binaryajax.js),方便获取图片的exif信息,通过获取exif的信息来确定图片旋转的角度(使用了exif.js),然后再进行图片相应的旋转处理。解决方法请戳我
  2. IOS中,当图片的大小大于 2MB时,会出现图片压扁的情况,这个时候需要重置图片的比例。解决方法请戳我
  3. 利用FileReader,读取图片的过程需要花费一定时间,将图片数据注入到canvas画布中需要一定时间,图片压缩的过程中,图片越大,CPU计算消耗的时间也越长,可能会出现顿卡的情况。总之,就是这个过程当中需要花费一定时间。
  4. IOS8.1的版本中有个FileReader的bug: FileReader读取的图片转化为Base64时,字符串为空,具体的问题描述请戳我

步骤4,文件上传有2种方式:

  1. 将图片转化为base64
  2. 将图片数据转为Blob对象,使用FormData上传文件

方式1可以通过xhr ajax或者xhr2 FormData进行提交。

方法2这里就有个大坑了。具体描述请戳我

简单点说就是:Blob对象是无法注入到FormData对象当中的。

当你拿到了图片的data uri数据后,将其转化为Blob数据类型

    var ndata = compress(img);
    ndata = window.atob(ndata); //将base64格式的数据进行解码
    
    //新建一个buffer对象,用以存储图片数据
    var buffer = new Uint8Array(ndata.length);
    for(var i = 0; i < text.length; i++) {
        buffer[i] = ndata.charCodeAt(i);
    }
    
    //将buffer对象转化为Blob数据类型
    var blob = getBlob([buffer]);
    
    var fd = new FormData(),
        xhr = new XMLHttpRequest();
    fd.append('file', blob);
    
    xhr.open('post', url);
    xhr.onreadystatechange = function() {
        //do something
    }
    xhr.send(fd);

在新建Blob对象中有需要进行兼容性的处理,特别是对于不支持FormData上传blob的andriod机的兼容性处理。具体的方法请戳我
主要实现的细节是通过重写HTTP请求。


2月19日更新

在安卓机器中,部分4.x的机型, 在webview里面对file对象进行了阉割,比如你拿不到file.type的值。

2月22日更新

Android4.4<input type="file">由于系统WebViewopenFileChooser接口更改,导致无法选择文件,从而导致无法上传文件. bug描述请戳我

封装好的github库,请戳我,如果觉得文章不错,请不要吝啬你的star~~


如果觉得我的文章对你有用,请随意赞赏
已赞赏

你可能感兴趣的

43 条评论
all2005 · 2016年08月03日

老司机

+1 回复

wheato · 2016年08月11日

可以测试一下android, android有些系统版本toDataURL()是不支持jpeg格式导出的,需要引入第三方库解决

+1 回复

0

嗯,fex-team提供的webuploader提供了jpegcoder.jsandriodpatch.js主要是来解决这个问题的。但是部分安卓机型对file对象进行了阉割,拿不到。

苹果小萝卜 作者 · 2017年03月17日
Thinking80s · 2016年08月04日

+1

回复

左源 · 2016年12月19日

老司机雷锋,赞

回复

李惟 · 2017年03月02日

有部分手机浏览器连FileReader都不支持,直接从第一部就over掉了

回复

0

请问有相应的手机型号吗?

苹果小萝卜 作者 · 2017年03月02日
0

@苹果小萝卜 红米自带浏览器、华为P8青春版 firefox

李惟 · 2017年03月02日
1

@李惟 嗯,是。做一些内嵌到webview里面的H5页面的时候,遇到这种case的话,让端上提供bridge来进行图片的压缩,然后转成base64返回给你。然后再去完成图片的上传。

苹果小萝卜 作者 · 2017年03月16日
BANG · 2017年03月16日

你好 我昨天用了你的demo 5s上传不了的bug

回复

0

你好, 你是用的我github上的封装好的库吗? 链接点我

苹果小萝卜 作者 · 2017年03月16日
0

https://github.com/gokercebec... 我用的这个 弄错了 谢谢

BANG · 2017年03月16日
0

@BANG 嗯,我把那个库以及需要的库都打包到一个文件了,你试试我放在github上的那个试试

苹果小萝卜 作者 · 2017年03月16日
Hou · 2017年05月24日

之前试了好多方法,仿佛回到了解微积分的时候... 总之,感谢

回复

心碎烏托邦 · 2017年08月23日

用canvas合成二维码与一张背景图片,在浏览器上可以显示,但是在手机浏览器、微信就显示不出来,这是什么原因?

回复

0

有没有报错啥的?

苹果小萝卜 作者 · 2017年08月23日
0

@苹果小萝卜 没报错,在浏览器没有报错

心碎烏托邦 · 2017年08月23日
0

@心碎烏托邦 https://segmentfault.com/q/10...,我提问题,吧代码贴出来了,帮忙看一下呀呀

心碎烏托邦 · 2017年08月23日
东成 · 2017年09月27日

现在最新的IOS系统 在微信浏览器 能实现图片压缩上传吗?请解答一下,谢谢!

回复

0

可以啊,为什么不行呢?

苹果小萝卜 作者 · 2017年09月27日
末央 · 2017年11月10日

你好,修改头像为什么在安卓低版本改不了是什么原因导致的

回复

0

你好。能提供具体信息吗?

苹果小萝卜 作者 · 2017年11月10日
undefined · 2017年12月18日

请问能刘一个联系方式吗,对于上传图片我有问题想请教你一下

回复

0

请看github

苹果小萝卜 作者 · 2017年12月18日
0

压缩过后生成的二进制,我直接放在img的src是可以在页面中显示的,但是用base64转码传到后台,解析出来生成的图片是打不开,显示图片损坏或者图片过大

undefined · 2017年12月18日
0

你是怎么解析的

苹果小萝卜 作者 · 2017年12月19日
sdhpc · 2月7日

您好请问怎么解决上传base64的时候字符串过长,导致部分机型上传失败的问题呢???急急急,在线等!!!

回复

0

请问解决了吗,

funny · 9月7日
sdhpc · 2月7日

请问怎么解决H5上传图片,就是base64的时候 字符串过长导致部分机型上传失败或者卡死的问题呢。根据我们需求的反馈发现这个问题好像是iPhone出现的几率大许多。。。很着急,。希望能够给回复亲~~

回复

0

你好,base64过长的话,iphone会卡死吗?这个问题暂时还未遇到过

苹果小萝卜 作者 · 3月15日
PLAxiaoxin · 3月15日

我这边gif图 在苹果7,8上面黑屏的bug你见过没?

回复

PLAxiaoxin · 3月15日

blob数据格式 可以通过canvas.toBlob进行转码 详细的可以看张鑫旭的博客 http://www.zhangxinxu.com/wor...

回复

暖色๑花雨 · 6月11日

请问支持require加载吗

回复

载入中...