浏览器中拍照、上传
前不久碰到了这样的需求:在网页中调用摄像头,拍照后上传图片。
实现并不难,可直接参考webRTC给出的demo。但一来网上搜出来的文章都比较早,很多API已不再适用;二来涉及的东西较多,还是值得总结一下。
方法概述
- 通过navigator.mediaDevices.getUserMedia()方法获取摄像头媒体流,用
<video>
标签展示 - 拍照时借助canvas API中的drawImage()方法截取图片
- 保存或上传,可使用toDataURL()方法或toBlob()方法获取图片
获取媒体流
MediaDevices.getUserMedia()
方法可获取媒体流,该方法返回的是一个Promise。
1. 基本用法
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
/* use the stream */
}).catch(function(err) {
/* handle the error */
});
其中参数constraints
是一个MediaStreamConstraints对象,其属性audio,video的值可配置为boolean
或MediaTrackConstraints
对象,默认为false
。
具体配置可参考MediaTrackConstraints或直接看标准。可使用navigator.mediaDevices.getSupportedConstraints()
方法检测浏览器对这些限制的支持情况。
2. 权限
调用getUserMedia
方法时会根据constraints
中的配置提示用户授权以使用摄像头、麦克风。
3. 兼容性
之前的API是navigator.getUserMedia(constraints, successCallback, errorCallback)。Chrome和FireFox在早期分别实现了带前缀的方法navigator.webkitGetUserMedia
和navigator.mozGetUserMedia
。
对于旧版本的浏览器,可使用adapter.js来polyfill。
在Chrome中,调用getUserMedia
要求必须使用安全的协议(如https
),否则会报错。详情请点这里。
4. 视频显示
需要注意的是,在旧版本的浏览器中<video>
并不支持srcObject。1
var video = document.querySelector('video');
// Older browsers may not have srcObject
if ("srcObject" in video) {
video.srcObject = stream;
} else {
// Avoid using this in new browsers, as it is going away.
video.src = window.URL.createObjectURL(stream);
}
拍照
截取视频的当前帧,显示在canvas上。
var video = document.querySelector('video');
var button = document.querySelector('button');
button.onclick = function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').
drawImage(video, 0, 0, canvas.width, canvas.height);
};
图片上传
canvas提供了toDataURL()
和toBlob()
两个方法来获取其内容。2
toDataURL
该方法返回的是一个data URI。默认格式为image/png
,质量为0.92。
var dataURL = canvas.toDataURL();
toBlob
该方法需要在回调中获取Blob对象。
canvas.toBlob(function(blob){...}, 'image/jpeg', 0.95); // JPEG at 95% quality
可以通过toDataURL()
方法获取Blob对象,或构造File对象,通过FormData上传。对于第三方SDK上传,通常也会提供File
或Blob
为参数的接口。
dataURL与Blob的转换
data URI是base64
或ASCII
编码过的数据3。在本文所述需求的情景下,dataURL与Blob的转换本质上是base64的编码与解码,用到的函数有atob()和btoa()。
下面以dataURL转Blob为例:
由于File
或Blob
都可由ArrayBuffer构造,因此只需将data URI字符串转化为ArrayBuffer
对象。ArrayBuffer
有不同的数据格式视图,统称为TypedArray,下面的方法选择的是Uint8Array(无符号8位整数数组)。4
function dataURItoArrayBuffer(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var bufferArray = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
bufferArray[i] = byteString.charCodeAt(i);
}
return bufferArray;
}
事实上,Node.js中的Buffer
类即实现了Uint8Array
的API,可以通过Buffer.from()方法来构造。浏览器中也可通过引用buffer实现对Buffer
类的支持。