progrape / mobile-upload-demo Goto Github PK
View Code? Open in Web Editor NEWmobile upload demo
mobile upload demo
WeUI QQ 交流群一直有人咨询,微信内的 Web 应用或者普通的移动端 Web 应用,如何上传图片?这里做个简要总结。
在浏览器端,传图/传文件的方式,就是 HTML 的 <input type="file" />
控件,而如果你的 Web 应用是特定运行在微信内的,那么还可以选择微信 App 提供的、可以通过 JSSDK 调用的传图功能。
传图过程可以分为:
选择图片,通过调用 chooseImage
接口实现:
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
// 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
var localIds = res.localIds;
}
});
选择完图片后,会得到图片的标识,因为可能有多张图片,所以是个 ID 列表,需要遍历出来。该 localId
可以当成 img
标签的 src
属性使用。
也就是说,如果需要生成预览图,只需要创建 img
标签,把 localId
塞进 src
属性就可以显示出来了。例如:
var localIds = res.localIds;
for(var i = 0; i < localIds.length; i++) {
var localId = locals[i];
var img = new Image();
img.src = localId;
// 塞到 DOM 结构
// ...
}
这样就可以在页面中预览选中的图片了。接下来就是把图片传到后端,触发条件可以是选完图马上自动传,也可以等用户点击按钮触发上传。
wx.uploadImage({
localId: '123abc234abc3452abc',
isShowProgressTips: 1,
success: function (res) {
// 图片传成功到微信服务器,得到一个 id,可以通过这个 id 在后端从微信服务器把图片下载下来
var serverId = res.serverId;
}
});
具体细节请参考 JSSDK 文档
然后来看看通用的 <input type="file" />
方式传图,其实说是通用,在 Android 4.4.1 和 Android 4.4.2 的版本,把这个功能给去掉了,点击没反应,无法调起选择图片的弹框。这个问题如果要解决,需要客户端的配合,具体方案可以网上搜索。
所幸的是,在微信 Android 客户端,使用的是 X5 内核,X5 帮忙填补了这个坑,如果你的应用只是在微信内使用,那么不需要考虑这个问题。
首先,页面中放置一个 input
控件(这里只探讨功能实现,不关注 UI,UI 层面可以使用 WeUI 的组件):
<input type="file" id="file" />
然后用户点击这个控件,就可以调起选图或者拍照了。那么如何得到选择的图片呢?如何生成预览图呢?这需要监听这个控件的 change
事件,然后使用 FileReader
对象来读:
document.getElementById('file').addEventListener('change', function (e) {
var file = e.target.files[0];
if (file && /^image\//i.test(file.type)) {
var reader = new FileReader();
reader.onloadend = function () {
// 图片的 base64 格式, 可以直接当成 img 的 src 属性值
var dataURL = reader.result;
var img = new Image();
img.src = dataURL;
// 插入到 DOM 中预览
// ...
};
// 读出base64格式
reader.readAsDataURL(file);
}
}, false);
读取图片,生成预览图完成了,那么怎样进行上传呢?这里有两种方式进行上传,第一种是直接向后端 post 提交 base64 格式的数据;第二种是构造 blob 二进制格式文件,以传文件的形式上传。个人非常推荐第二种方式。
这种方式,就是把图片当成字符串进行提交,不过缺点就是提交的字符串过大,很可能会被后端拒绝,nginx、PHP 都有限制默认提交数据大小的。
$.post('/upload', {data: dataURL}).success(function (res){
// 拿到提交的结果
}).error(function (err){
console.error(err);
});
推荐使用这种方式,图片很大都可以传。在图片预览阶段,我们拿到的是 base64 格式的数据,这里可以转成:
/**
* dataURL to blob, ref to https://gist.github.com/fupslot/5015897
* @param dataURI
* @returns {Blob}
*/
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {type: mimeString});
}
然后构造 FormData
对象,填充二进制文件数据,通过 ajax 的方式进行提交:
var fd = new FormData();
var blob = dataURItoBlob(dataURL);
fd.append('file', blob);
$.ajax({
type: 'POST',
url: '/upload',
data: fd,
processData: false,
contentType: false,
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
console.log('进度', percentComplete);
}
}, false);
return xhr;
}
}).success(function (res) {
// 拿到提交的结果
}).error(function (err) {
console.error(err);
});
注意:使用 jquery 的方法,不要漏了指定 processData
和 contentType
为 false
。
这样就把数据提交到后端了。在 Chrome 的 Nework 面板,可以看到请求带有图片数据。
接下来就是后端接收了,这里以 Node.js 的接收为例子:
const koa = require('koa');
const mount = require('koa-mount');
const parse = require('co-busboy');
const fs = require('fs');
const path = require('path');
const app = koa();
app.use(mount('/upload', function *(next) {
if ('POST' !== this.method) {
return yield next;
}
const parts = parse(this);
var part;
while (part = yield parts) {
// 保存到 upload 目录, 以当前的时间戳为文件名
const ext = path.extname(part.filename) || '.png';
const filename = path.join(__dirname, 'upload', new Date().getTime() + ext);
const stream = fs.createWriteStream(filename);
part.pipe(stream);
console.log('uploading %s -> %s', part.filename, stream.path);
}
this.body = {
ret: 0,
msg: 'ok'
};
}));
app.listen(3000, function (){
console.log('listening on port 3000');
});
最终传图功能完成。
在 PC 端,大部分图片都会在 1M
大小以内,而且 PC 端不用太在意流量问题,所以通常不用考虑压缩的问题。而在移动端,如果用户使用的是移动网络,即使你不考虑网速,也要考虑一下流量问题。
下面来看看移动端 Web 环境下如何在上传前压缩图片。
回顾一下刚才选择完图片后生成预览图的步骤:
document.getElementById('file').addEventListener('change', function (e) {
var file = e.target.files[0];
if (file && /^image\//i.test(file.type)) {
var reader = new FileReader();
reader.onloadend = function () {
// 图片的 base64 格式, 可以直接当成 img 的 src 属性值
var dataURL = reader.result;
var img = new Image();
img.src = dataURL;
// 插入到 DOM 中预览
// ...
};
// 读出base64格式
reader.readAsDataURL(file);
}
}, false);
在 reader.onloadend
回调方法里面,我们可以读到图片的 base64
格式数据,也 new
了一张图片,怎样进行图片压缩?这时候我们要请出 canvas
。先来整理一下思路:
new
出来的 Image
对象,我们监听它的 onload
事件canvas
,尺寸设置成上一步骤算出来的压缩后的图片尺寸drawImage
方法,把图片绘制到 canvas
中toDataURL
,取出 base64
格式的数据代码如下:
var img = new Image();
img.onload = function () {
// 当图片宽度超过 400px 时, 就压缩成 400px, 高度按比例计算
// 压缩质量可以根据实际情况调整
var w = Math.min(400, img.width);
var h = img.height * (w / img.width);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 设置 canvas 的宽度和高度
canvas.width = w;
canvas.height = h;
// 把图片绘制到 canvas 中
ctx.drawImage(img, 0, 0, w, h);
// 取出 base64 格式数据
var dataURL = canvas.toDataURL('image/png');
// ...
};
img.src = reader.result;
需要注意的是,通过 canvas
绘制的图片,低版本 iOS 会出现比例不正确的情况,请参考 https://github.com/stomita/ios-imagefile-megapixel ,本文不做探讨。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.