SlideShare a Scribd company logo
1 of 13
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
欢迎靠谱人才来靠谱团队

More Related Content

Similar to Html5 create thumbnail

[DCTPE2010] Drupal 遇上行動網路服務
[DCTPE2010] Drupal 遇上行動網路服務[DCTPE2010] Drupal 遇上行動網路服務
[DCTPE2010] Drupal 遇上行動網路服務Drupal Taiwan
 
跟我一起 Hold 住 iOS5
跟我一起 Hold 住 iOS5跟我一起 Hold 住 iOS5
跟我一起 Hold 住 iOS5Yang SuJung
 
第一次 Mobile App 就上手
第一次 Mobile App 就上手第一次 Mobile App 就上手
第一次 Mobile App 就上手Ying-Hsiang Liao
 
打造你的第一個 iOS App
打造你的第一個 iOS App  打造你的第一個 iOS App
打造你的第一個 iOS App 彼得潘 Pan
 
About Mobile Web Development Thing
About Mobile Web Development ThingAbout Mobile Web Development Thing
About Mobile Web Development ThingYu-Wei Chuang
 
《氪周刊:互联网创业必读》(79期)
《氪周刊:互联网创业必读》(79期)《氪周刊:互联网创业必读》(79期)
《氪周刊:互联网创业必读》(79期)36Kr.com
 
《氪周刊:互联网创业必读》(第83期)
《氪周刊:互联网创业必读》(第83期)《氪周刊:互联网创业必读》(第83期)
《氪周刊:互联网创业必读》(第83期)36Kr.com
 

Similar to Html5 create thumbnail (7)

[DCTPE2010] Drupal 遇上行動網路服務
[DCTPE2010] Drupal 遇上行動網路服務[DCTPE2010] Drupal 遇上行動網路服務
[DCTPE2010] Drupal 遇上行動網路服務
 
跟我一起 Hold 住 iOS5
跟我一起 Hold 住 iOS5跟我一起 Hold 住 iOS5
跟我一起 Hold 住 iOS5
 
第一次 Mobile App 就上手
第一次 Mobile App 就上手第一次 Mobile App 就上手
第一次 Mobile App 就上手
 
打造你的第一個 iOS App
打造你的第一個 iOS App  打造你的第一個 iOS App
打造你的第一個 iOS App
 
About Mobile Web Development Thing
About Mobile Web Development ThingAbout Mobile Web Development Thing
About Mobile Web Development Thing
 
《氪周刊:互联网创业必读》(79期)
《氪周刊:互联网创业必读》(79期)《氪周刊:互联网创业必读》(79期)
《氪周刊:互联网创业必读》(79期)
 
《氪周刊:互联网创业必读》(第83期)
《氪周刊:互联网创业必读》(第83期)《氪周刊:互联网创业必读》(第83期)
《氪周刊:互联网创业必读》(第83期)
 

Html5 create thumbnail

  • 1. Mobile web优化图片上传(前端缩略图) 徐海亮 @ 长沙墨研 && 發燒網 2013/08/01 手机web, 上传图片遇到的问题(上传速度慢/ 图片旋转/ISO6的bug)及解决方案
  • 2. 遇到的问题: 现在的手机越来越牛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”交给后端生成 缩略图?
  • 3. 前端生成缩略图的大体思路如下: 首先判断是否支持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, 一个用来读取本地图片, 一个用来生成缩 略图.
  • 5. 遇到的问题: 1. 选相册里图片上传没有问题,但直接拍照上传(iOS 6 的内置相机),图片截取的 不完整(被压扁了), 比如: 1. 即使选相册的图片能成功生成缩略图, 但是也有问题, 生成后的缩略图有些是东 倒西歪或者直接上下颠倒了. -->
  • 6. 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
  • 7. 用到的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)
  • 8. 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/
  • 9. 附加内容: 判断是否支持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 也同样).
  • 10. 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的部分把代码重新写一 份.
  • 11. 使用android低版本的童鞋们哭了~ (根本没考虑他们的感受). 问题: canvas.toDataURL() 会返回空数据(应该是android4.0以下的版本, 测试 2.3.7是不支持的), 有个todataurl-png-js的解决方案(未测试). 还有FileReader/createObjectURL等没有测试支持情况. 所以照顾低版本android的用户, 还有一些路要走. 还要思考这个是不是有必要? 有没有那么多的用户群体. 因为现在的方案在不支持前端生成缩略图的情况, 就用后端生成, 所以 android低版本也不会存在问题.
  • 12. 普通的web表单, 上传图片靠后端来生成缩略图很平 常, 但有了HTML5, 针对移动Web开发可以考虑使用 前端生成缩略图了. Thanks!
  • 13. 希望大家多关注 长沙墨研 在这里可以经常接到技术含量较高的HTML5效果: 比如: 刮刮卡/类似LINE Card的图片贺卡对话框/mardown编辑器(拖曳上传/截 图直接粘贴) http://cdpn.io/wcIlt http://sandbox.runjs.cn/show/igpzkemh 欢迎靠谱人才来靠谱团队