- Mobile web优化图片上传(前端缩略图) 徐海亮 @ 长沙墨研 && 發燒網 2013/08/01 手机web, 上传图片遇到的问题(上传速度慢/ 图片旋转/ISO6的bug)及解决方案
- 遇到的问题: 现在的手机越来越牛X(拍摄功能比数码相机还厉害~) 图片体积也越来越大 上面是iPhone5/4拍摄的照片, 1-2M的大小是小case了,iOS6一般2M以上. 一个几M的图, 也许我们只是用来生成一个100*100的小图, 上传到后端再 生成缩略图就大大的浪费了, 而且提交表单的时间也比较久, 对用户体验 也不好. Safari on iOS supports file uploading—that is, <input type=“file”> elements—on iOS 6 and later. (iOS6也支持上传了) HTML5应用越来越多的当下, 还继续使用input type=“file”交给后端生成 缩略图?
- 前端生成缩略图的大体思路如下: 首先判断是否支持fileReader(支持fileReader, canvas就不在话下了) 不支持的话: 不做任何操作, 默认的input type="file"上传, 靠后端生成缩 略图. 支持的情况: input change时, 判断选择的是图片, 就创建一个隐藏的 canvas, 并把图片画到canvas里. 因为要生成缩略图, 所以在canvas里画图的时候, 控制剪裁坐标和被剪裁 的宽高就OK了. 最后 canvas.toDataURL 转成base64, post到后端(先把input type=“file” 移除, 再生成个新的input type=“hidden”储存图片数据), 再提交表单, 后 端接收后直接保存为图片就OK了. 主要用到: FileReader和canvas, 一个用来读取本地图片, 一个用来生成缩 略图.
- 应该差不多了吗? 其实还有bug等着你~ 先看一个有bug的案例: https://github.com/kairyou/html5-make-thumb/tree/old/
- 遇到的问题: 1. 选相册里图片上传没有问题,但直接拍照上传(iOS 6 的内置相机),图片截取的 不完整(被压扁了), 比如: 1. 即使选相册的图片能成功生成缩略图, 但是也有问题, 生成后的缩略图有些是东 倒西歪或者直接上下颠倒了. -->
- 1 是iOS6的bug, 较大的图片可能会发生(大概>2M), 已经有人提交bug给 Apple了(iOS7未测试). 2 是因为相机拍摄的图片本身可能就是反转的, 拍摄的图片里EXIF信息的 orientation记录了图片的转向(取值范围是1/3/6/8, 一般是:1/3/6/8). 默认竖拍的时候其实就是翻转的, 这时的orientation=6(相当于向左转90度, -90deg) 倒立拍照时=8 (向右90度, 90deg). 横着拍时: iPhone的按钮在右侧, (orientation=1, 0deg )才是默认的不反转的. iPhone的按钮在左侧, (orientation=3, 180deg )是上下倒立的. 在电脑/手机查看这些图片都是正确的方向? 看图软件大多都支持自动旋转. 另外还有比较少见的:2/4/5/7(原图经过反转了), 比如: 原图: http://www.csharphelper.com/howto_show_exif_orientations.png http://beradrian.files.wordpress.com/2008/11/operations.gif
- 用到的js: 1. megapix-image.js, https://github.com/stomita/ios-imagefile-megapixel 2. exif.js, https://github.com/jseidelin/exif-js 3. binaryajax.js, https://github.com/jseidelin/binaryajax/ 首先自己input file选择的图片readAsDataURL获取到base64的字符串, 再用 binaryajax获取图片的二进制数据(data-url to a binary string). 然后根据二进制数据利用exif.js获取exif信息(主要是exif.Orientation), 再用 MegaPixImage去生画图, 再读取新的base64图片, 提交给后端储存. var mpImg = new MegaPixImage(file); mpImg.render(canvas, { maxWidth: opts.width, maxHeight: opts.height, orientation: orientation }); 也许有人已经想到为什么不直接用readAsBinaryString读取二进制, 然后再用 exif.js获取exif数据, 还能省去一个js, 原因: 1. exif.js对binaryajax的方法有依赖; 2. 生成的缩略图是空白的, (猜测是没有在image.onload后执行生成缩略图导 致, 因为这里得到的不是图片的base64数据, 还要另外花费代码去转换数据 类型,再去加载, 那就做了更多的事情, 没意义了 ) 还有, Binaryajax兼容性更佳(里面还有兼容IE6的code)
- Megapix原理: 1. 先按图片的exif信息进行旋转. 2. 首先检测图片是否绘制的时候出现异常: 对于尺寸大于1024*1024的图片, 把图片插到canvas, 只画图片的右上角 1*1像素的部分, 然后getImageData读取这个点的RGBA值, 取返回的 array[3](第4个值是透明度), 等于0说明没绘制上去, 出现异常了. 这时的异常是指图片高宽各缩了一半. 3. 后面要检测图片在垂直方向压扁(jpg类型的图片才需要检测): 画宽为1,高为图片实际宽度(如果第1部检测出异常, 宽高要减半)的矩形, 把图片插入后, 再判断最下面1*1是否透明(原图的左下角), 如果透明再到 高度减半的位置判断(1/2, ¼, 1/8…), 直到遇到不透明的点. 根据图片顶部 到这个点的距离/图片高度得到1个比率. 4. 开始绘制, 如果遇到2或3图片被拉伸的情况, 这里就相应的拉伸回正常 的比例. 新的方案: https://github.com/kairyou/html5-make-thumb/
- 附加内容: 判断是否支持FileReader: $.support.filereader = !!(window.File && window.FileReader && window.FileList && window.Blob); Php处理post过来的图片 // save img $img = $_POST['base64']; $uploadFile = $_FILES['upfile']; $target = 'tmp.jpg’; if (isset($img)) { # dataURI if (preg_match('/data:([^;]*);base64,(.*)/', $img, $matches)) { $img = base64_decode($matches[2]); file_put_contents($target, $img); } } else { # 普通上传 if (isset($uploadFile) && is_uploaded_file($uploadFile['tmp_name']) && $uploadFile['error'] == 0) { move_uploaded_file($uploadFile['tmp_name'], $target); } } Megapix里面的一些奇淫技巧: num >> 1 代替 num *0.5 (右移运算,目标值*2的1次方=num; 另外, 左移类似 5<<4 二进制左移4位(相当与 此数乘以2的4次方),也就是80=5 *16 ) 参见:http://t.cn/zONS2VQ num >> 0 代替 Math.floor()来取整(位移的结果没有小数, 结果向下取整, 所以这里 num<<0 也同样).
- Todo: 1. 图片加水印, 可以是图片水印或文字水印, 文字水印可以设置字体/大 小/颜色等样式; 可以多张水印; 可以设置水印的位置(居中或四个角). 2. 可选生成缩略图的方式, 相当于css3的background-size: contain | cover | auto(效果:http://t.cn/zQXYpYr), auto:不强制拉伸图片, 图片 水平垂直居中(大图被切掉, 小图四周留空), cover: 填满元素, 小图等 比拉伸, 超过的部分被切掉, 不会有空白, contain:等比缩小图片来适 应元素的尺寸, 可能会产生空白, 图片不会被切掉. 3. Canvas里的图片实际尺寸小于缩略图规定的尺寸(auto或contain时), 这时会有空白区域, 可选是否可以把空白也切掉(最后目标图片的尺寸 可能会不统一); 可选空白区域的背景色(比如白色, 黑色, 透明. 为了照 顾iOS6, 要先生成缩图在填充背景, 因为前面提到判断图片绘制异常 的时候会读取1*1像素判断是否透明). 这些功能在之前写的生成缩略图里面基本都有了, 新的方案由于依赖 megapix, 所以大概会参考megapix解决iOS6bug的部分把代码重新写一 份.
- 使用android低版本的童鞋们哭了~ (根本没考虑他们的感受). 问题: canvas.toDataURL() 会返回空数据(应该是android4.0以下的版本, 测试 2.3.7是不支持的), 有个todataurl-png-js的解决方案(未测试). 还有FileReader/createObjectURL等没有测试支持情况. 所以照顾低版本android的用户, 还有一些路要走. 还要思考这个是不是有必要? 有没有那么多的用户群体. 因为现在的方案在不支持前端生成缩略图的情况, 就用后端生成, 所以 android低版本也不会存在问题.
- 普通的web表单, 上传图片靠后端来生成缩略图很平 常, 但有了HTML5, 针对移动Web开发可以考虑使用 前端生成缩略图了. Thanks!
- 希望大家多关注 长沙墨研 在这里可以经常接到技术含量较高的HTML5效果: 比如: 刮刮卡/类似LINE Card的图片贺卡对话框/mardown编辑器(拖曳上传/截 图直接粘贴) http://cdpn.io/wcIlt http://sandbox.runjs.cn/show/igpzkemh 欢迎靠谱人才来靠谱团队
转自:http://www.slideshare.net/99leon/html5-create-thumbnail