移动端Web上传图片实践
2016-10-10 11:42
381 查看
原文链接
其他链接: http://tgideas.qq.com/webplat/info/news_version3/804/808/811/m579/201409/278736.shtml
从iOS 6+、Android 3+开始
(来源http://mobilehtml5.org/),移动端可以通过网页中的
一些设备在拍照时明明是竖着拍的(右),传到服务端后从图片查看器中看到的是横着的(左),而在一些图片处理工具或是浏览器中通过http协议看到的却是正常的,原因是在照片的exif中的Orientation属性控制了照片的旋转方向。
大部分图片查看器和编辑工具都会去根据这个属性控制照片方向,而windows自带的查看器等工具并不理睬他。
为什么要有Orientation属性呢,我并没有找到官方的解释,见到种说法挺有道理:几乎所有的摄像头在出场的时候成相芯片都是有方向的,拍出来的照片的像素都是默认方向的。如果每拍一张照片就对这些像素进行旋转,如果数码相机每秒连拍20张来算,旋转操作将会非常耗时。更聪明的做法是拍照时只记录一个方向,然后显示的时候按方向显示出来即可。因此exif定义了一个标准的方向参数,只要读图的软件都来遵守规则,加载时候读取图片方向,然后做相应的旋转即可。这样既可以达到快速成像的目的,又能达到正确的显示。
为了图片显示不出问题,我们得修改Orientation属性。首先想到的是服务端来修正方案,对于图片来说exif信息不是必须的,可以根据Orientation的值来对照片进行手动矫正的操作,然后再去掉exif,类似这样:
更好的方式是用一些图片工具自动处理,GraphicsMagick和Imagemagick用来处理这个再合适不过了,他俩都可以用对图像进行旋转、裁剪、缩放、替换颜色;添加文本、水印、图形等常见操作,GraphicsMagick是从Imagemagick分支出来的,他俩有着几乎一样的API,可以在命令行工具中轻松操作。这里我用的是GraphicsMagick,他更轻便,易装易用。在Java、PHP、Nodejs等常见后端语言中可以用相关库轻松的操作GraphicsMagick的API。
后端以Nodejs为例。首先你需要在你的机器上安装GraphicsMagick。 然后npm install gm 模块就可以了。gm提供的接口非常友好,你只要
这样就已经完成了图片的自动修正方向和压缩尺寸的工作。
传统提交表单方式放在今天已经不能忍了,XHR2中支持把文件放在Formdata对象中异步提交,只考虑移动端,就可以舍弃iframe之类的兼容方案了。核心代码这样:
XHR2中还可以通过process事件来监听进度,实现类似进度条的功能
用FormData发送的请求头中你的Content-Type 会变成这样 multipart/form-data; boundary=----WebKitFormBoundaryyqVkWF3PcCpAzZp9,如果上传时要附带参数也可以直接append到formData里。
然后Nodejs中可以用connect-busboy来接收文件,在express框架中大概是这样:
图片上传的主体工作算是完成了,不过现在手机随便拍张照片就是一两兆,wifi环境下不说,移动网络通过这方案上传照片就有点坑了。手机客户端中一般会先压缩图片再上传,Web中如何实现压缩后上传呢?
可以把图片读到canvas中,然后用canvas.toDataURL()接口输出画布的base64编码,再把base64编码转成Blob塞到Formdata里传到后端。这样即可以压缩图片减少流量,又可以在前端就修正图片旋转的问题。(Discuss:直接把base64传到后端是否可行呢,后面试一试)
canvasResize这个库已经把一切封装好了https://github.com/gokercebeci/canvasResize ,同时他依赖Exif.js 修正了因Orientation属性产生的旋转问题。前端主要的代码:
Nodejs中代码可以参考前面的,继续用connect-busboy模块接收文件。
实际测试一下iOS没问题,Android 4 有些机型不行,貌似修改过file的Blob数据发到服务端的数据字节就会为0 这是安卓的bug https://code.google.com/p/android/issues/detail?id=39882 。
网上有人给出的解决方案是用FileReader把文件读出来,然后把整个二进制文件当请求发到服务端,这种方式要附带参数的话只能放url里了。
后端在接收这些数据时,会是一段一段的,我是用的拼接的方式处理
实测一下,稍低端的的安卓上有点卡,毕竟处理一张图片的运算量可不小,目测目前用前端压缩上传方案的不多,至少微博触屏版 (http://m.weibo.cn/) 就是把原始图片直接上传的,这种方式是否适合直接使用或者还有哪些可以优化的地方有待验证。
这里有一个完整的demo https://github.com/xiangpaopao/mobile-upload-demo 包括上面提到的两种方案
使用的话,依次安装GraphicsMagick > npm install > node app.js
http://www.impulseadventure.com/photo/exif-orientation.html
http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
http://stackoverflow.com/questions/13055915/ios-6-safari-image-upload-orientation-wrong
http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html
http://www.cnblogs.com/smileEvday/p/imageInfo.html
https://github.com/fex-team/webuploader
http://elf8848.iteye.com/blog/382528
http://www.2fz1.com/post/node-js-canvas-base64/
其他链接: http://tgideas.qq.com/webplat/info/news_version3/804/808/811/m579/201409/278736.shtml
从iOS 6+、Android 3+开始
(来源http://mobilehtml5.org/),移动端可以通过网页中的
<input type="file">来拍照上传或是上传相册中的照片。 不过从图片上传到服务器后可能会遇到图片莫名其妙旋转的问题,如图
一些设备在拍照时明明是竖着拍的(右),传到服务端后从图片查看器中看到的是横着的(左),而在一些图片处理工具或是浏览器中通过http协议看到的却是正常的,原因是在照片的exif中的Orientation属性控制了照片的旋转方向。
//Exif 信息示例 Exif.Image.Make Ascii 6 Canon Exif.Image.Model Ascii 20 Canon PowerShot S40 Exif.Image.Orientation Short 1 top, left Exif.Image.XResolution Rational 1 180 Exif.Image.YResolution Rational 1 180 Exif.Image.ResolutionUnit Short 1 inch Exif.Image.DateTime Ascii 20 2003:12:14 12:01:44 Exif.Image.YCbCrPositioning Short 1 Centered ......
大部分图片查看器和编辑工具都会去根据这个属性控制照片方向,而windows自带的查看器等工具并不理睬他。
为什么要有Orientation属性呢,我并没有找到官方的解释,见到种说法挺有道理:几乎所有的摄像头在出场的时候成相芯片都是有方向的,拍出来的照片的像素都是默认方向的。如果每拍一张照片就对这些像素进行旋转,如果数码相机每秒连拍20张来算,旋转操作将会非常耗时。更聪明的做法是拍照时只记录一个方向,然后显示的时候按方向显示出来即可。因此exif定义了一个标准的方向参数,只要读图的软件都来遵守规则,加载时候读取图片方向,然后做相应的旋转即可。这样既可以达到快速成像的目的,又能达到正确的显示。
修正图片方向的问题
为了图片显示不出问题,我们得修改Orientation属性。首先想到的是服务端来修正方案,对于图片来说exif信息不是必须的,可以根据Orientation的值来对照片进行手动矫正的操作,然后再去掉exif,类似这样:switch(exif['Orientation']){ case 2: image->save; break; case 3: image->rotate(-180)->save; break; case 4: image->rotate(180)->save; break; ······ }
更好的方式是用一些图片工具自动处理,GraphicsMagick和Imagemagick用来处理这个再合适不过了,他俩都可以用对图像进行旋转、裁剪、缩放、替换颜色;添加文本、水印、图形等常见操作,GraphicsMagick是从Imagemagick分支出来的,他俩有着几乎一样的API,可以在命令行工具中轻松操作。这里我用的是GraphicsMagick,他更轻便,易装易用。在Java、PHP、Nodejs等常见后端语言中可以用相关库轻松的操作GraphicsMagick的API。
后端以Nodejs为例。首先你需要在你的机器上安装GraphicsMagick。 然后npm install gm 模块就可以了。gm提供的接口非常友好,你只要
gm('/path/to/img.jpg') .autoOrient() .resize(240, 240) .write('/path/to/new.jpg', function (err) { if (err) ... })
这样就已经完成了图片的自动修正方向和压缩尺寸的工作。
异步上传图片
传统提交表单方式放在今天已经不能忍了,XHR2中支持把文件放在Formdata对象中异步提交,只考虑移动端,就可以舍弃iframe之类的兼容方案了。核心代码这样:var xhr = new XMLHttpRequest(); var formData = new FormData(); formData.append('file', input.files[0]); xhr.open('POST', form.action); xhr.send(formData);
XHR2中还可以通过process事件来监听进度,实现类似进度条的功能
xhr.onprogress = updateProgress; xhr.upload.onprogress = updateProgress; function updateProgress(event) { if (event.lengthComputable) { var percentComplete = event.loaded / event.total; ...... } }
用FormData发送的请求头中你的Content-Type 会变成这样 multipart/form-data; boundary=----WebKitFormBoundaryyqVkWF3PcCpAzZp9,如果上传时要附带参数也可以直接append到formData里。
然后Nodejs中可以用connect-busboy来接收文件,在express框架中大概是这样:
var express = require('express'), http = require('http'), fs = require('fs'), busboy = require('connect-busboy'), gm = require('gm'); var app = express(); app.use(busboy()); ..... app.post('/upload', function(req, res) { req.busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { ...... file.on('end', function () { gm(filePath) .autoOrient() .thumbnail(200, 200) .write(fullname, function(err){ if (err) return console.dir(arguments) res.json({ ...... }); } ) }); file.pipe(fs.createWriteStream(filePath)); }); req.pipe(req.busboy); }); ...... app.listen(3001);
前端压缩图片
图片上传的主体工作算是完成了,不过现在手机随便拍张照片就是一两兆,wifi环境下不说,移动网络通过这方案上传照片就有点坑了。手机客户端中一般会先压缩图片再上传,Web中如何实现压缩后上传呢?可以把图片读到canvas中,然后用canvas.toDataURL()接口输出画布的base64编码,再把base64编码转成Blob塞到Formdata里传到后端。这样即可以压缩图片减少流量,又可以在前端就修正图片旋转的问题。(Discuss:直接把base64传到后端是否可行呢,后面试一试)
canvasResize这个库已经把一切封装好了https://github.com/gokercebeci/canvasResize ,同时他依赖Exif.js 修正了因Orientation属性产生的旋转问题。前端主要的代码:
var file = input.files[0]; canvasResize(file, { width: 300, height: 0, crop: false, quality: 100, callback: function(data, width, height) { var blob = canvasResize('dataURLtoBlob', data); var form = new FormData(); form.append('file',blob); $.ajax({ type: 'POST', url: server, data: form, contentType: false, processData: false, }).done(function (res) { ...... }).fail(function () { ...... }).always(function () { ...... }); } });
Nodejs中代码可以参考前面的,继续用connect-busboy模块接收文件。
实际测试一下iOS没问题,Android 4 有些机型不行,貌似修改过file的Blob数据发到服务端的数据字节就会为0 这是安卓的bug https://code.google.com/p/android/issues/detail?id=39882 。
网上有人给出的解决方案是用FileReader把文件读出来,然后把整个二进制文件当请求发到服务端,这种方式要附带参数的话只能放url里了。
var reader = new FileReader(); reader.onload = function() { $.ajax({ type: 'POST', url: server, data: this.result, contentType: false, processData: false, beforeSend: function (xhr) { xhr.overrideMimeType('application/octet-stream'); }, }).done(function (res) { ...... }).fail(function () { ...... }).always(function () { ...... }); }; reader.readAsArrayBuffer(file);
后端在接收这些数据时,会是一段一段的,我是用的拼接的方式处理
app.post('/upload', function(req, res) { var imagedata = ''; req.setEncoding('binary'); req.on('data', function (chunk) { imagedata += chunk }); req.on('end', function (chunk) { fs.writeFile(filePath, imagedata, 'binary', function(err){ if (err) throw err res.json({ ...... }); }) }); });
实测一下,稍低端的的安卓上有点卡,毕竟处理一张图片的运算量可不小,目测目前用前端压缩上传方案的不多,至少微博触屏版 (http://m.weibo.cn/) 就是把原始图片直接上传的,这种方式是否适合直接使用或者还有哪些可以优化的地方有待验证。
这里有一个完整的demo https://github.com/xiangpaopao/mobile-upload-demo 包括上面提到的两种方案
使用的话,依次安装GraphicsMagick > npm install > node app.js
参考
http://www.impulseadventure.com/photo/exif-orientation.htmlhttp://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
http://stackoverflow.com/questions/13055915/ios-6-safari-image-upload-orientation-wrong
http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html
http://www.cnblogs.com/smileEvday/p/imageInfo.html
https://github.com/fex-team/webuploader
http://elf8848.iteye.com/blog/382528
http://www.2fz1.com/post/node-js-canvas-base64/
相关文章推荐
- 移动端Web上传图片实践
- web端移动端上传图片,兼容微信
- 多图片上传预览实现以及移动端web多文件上传
- 移动端web无刷新上传图片【兼容安卓IOS】 推荐
- 移动端web开发之坑--IOS8下 上传图片点击取消后,弹出了软键盘遮挡输入框
- tomcat虚拟路径的实践---javaweb项目上传视频,图片到任意磁盘文件夹
- 移动端图片上传的实践
- [原]as3 flash web 应用 (8)图片批量上传之 向服务器发送和获取额外数据
- 尝试通过HttpWebRequest向TAOBAO批量发布商品及上传图片
- 尝试通过HttpWebRequest向TAOBAO批量发布商品及上传图片
- 如何将含有大量图片或公式的word文档上传到web服务器
- Web图片上传控件开发文档-JSP
- web编辑器远程图片自动上传
- 转载: 将图片从PDA端上传到Web服务器的方法
- eWebEditor V7.3 for asp版本编辑器带后的破解码版本,新增加防止图片木马上传
- eWebEditor V7.3 for asp.net版本编辑器带后台版本_新增加防止图片木马上传
- Web图片上传插件开发文档-PHP
- VS2008下web页面上传图片的自定义控件源码
- 如何将含有大量图片或公式的word文档上传到web服务器
- WEB中上传图片即使显示