admin管理员组文章数量:1558071
这几天公司有一个业务需求,就是需要在web前端(一般环境都是谷歌浏览器)调用本地摄像头进行拍照,然后上传到服务器上。有两种解决方案,一种是通过flash调用本地摄像头进行拍照,还有一种方式就是使用video
标签获取本地摄像头摄像,然后结合canvas
标签进行展示。
项目中前端使用的是layui,但并不影响迁移到其他框架,主语要把layui框架的一些语法换成迁移框架的即可。同时,项目中对启用摄像头,拍照生成照片(base64)等功能进行了封装,所以在使用此案例时可以直接导入封装好的js文件,然后再需要的地方引入即可。由于公司后端接口存在保密性,就不贴出来了,时间原因回头我自己写一个小的demo贴出来。
上传至服务器思路:用户点击拍照功能,会生成base64编码格式的文件,然后再转码成file文件格式(相当于input type=‘file’),实现上传。这样后端MultipartFile可以直接接收。(其实也可以转码成Blob(二进制)大对象,但是上传至服务器就是一个blob文件,图片回显的时候是直接通过url链接服务器地址进行回显,所以不能实现图片回显的功能。如果是图片下载,或者你不嫌麻烦在回显的时候先从图片服务器下载下来缓存到本地,也可以直接上传blob对象,后端MultipartFile也是可以接收的)
废话不多少,直接上代码。
前端Html:
<div class="layui-fluid layui-anim" id="camera" lay-title="摄像头测试">
<div class="layui-row">
<div class="layui-card" style="background-color:#f0f0f0;margin: auto auto;">
<div class="layui-card-body" style="padding: 0px;">
<div class="layui-row camera-show-pc">
<div class="bag_display_flex" style="justify-content: center;display: flex">
<div style="position: relative;">
<video id="v" style="background-color: #000000"></video>
<div class="camera-control">
<button type="button" id="stop" class="layui-icon layui-icon-stop camera-btn">关闭</button>
<button type="button" id="start" class="layui-icon layui-icon-triangle-r camera-btn">开始</button>
<button type="button" id="snap" class="layui-icon layui-icon-camera-fill camera-btn">拍照</button>
<button type="button" id="clear" class="layui-icon layui-icon-fonts-clear camera-btn">清空</button>
<button type="button" id="save" class="layui-icon layui-icon-save camera-btn">上传</button>
</div>
</div>
<div>
<canvas id="canvas" style="margin-left: 2px;background-color: #000000"></canvas>
</div>
</div>
<div class="layui-row" style="position: relative">
<div class="camera-canvas-group"></div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item" style="text-align: center;">
<button id="closeBtn" type="button" class="layui-btn layui-btn-primary" function="close">关闭</button>
</div>
</div>
//在使用前引入
<script type="text/javascript" src="${ctxPath!}/plugins/camvas/camvas.js"></script>
<script data-th-inline="javascript" type="text/javascript">
layui.use(['jquery','layer'], function () {
var $ = layui.jquery,
$view = $('#camera'),
config={},
myCamvas;
var layer = layui.layer;
init();
onClick();
function init() {
let scale = 120;//宽高比例倍数
config = {
video:{width:Number(scale*4),height:Number(scale*3)},//4:3
canvasId:'canvas',
videoId:'v',
imgType:'png',
quality:'1' //图片质量0-1之间
};
$('.camera-show-pc').css('width',config.video.width*2+20);
$view.find('#canvas').attr('width',config.video.width);
$view.find('#canvas').attr('height',config.video.height);
//拍照实例化
myCamvas = new camvas(config);
myCamvas.startCamera();
$view.find('#start').hide();
}
function onClick() {
//停止拍照
$view.find('#stop').click(function () {
myCamvas.stop()
$view.find('#stop').hide()
$view.find('#start').show()
$view.find('#snap').show()
})
//启动摄像头
$view.find('#start').click(function () {
myCamvas.startCamera();
$view.find('#stop').show()
$view.find('#start').hide()
})
//拍照
$view.find('#snap').click(function () {
myCamvas.drawImage(function drawImage(base64URL) {
let xsCamera = $('<div></div>').addClass('camera-canvas-item');
//这个可以实现连续拍照,公司业务需求只需要单张照片,如果想要连续拍照,把下面那行代码注释掉即可。
$('.camera-canvas-group').empty()
xsCamera.append($('<img>').attr('src',base64URL)).append($('<span></span>').addClass('layui-icon layui-icon-close-circle-fill canvas-item-del'));
$('.camera-canvas-group').prepend(xsCamera);
});
/*$view.find('#snap').hide();*/
})
//清空
$view.find('#clear').click(function () {
clearCanvas();
$view.find('.camera-canvas-group').empty()
})
//append方式添加节点直接click无效,点击显示大图
$view.find('.camera-canvas-group').on('click','.camera-canvas-item img',function () {
myCamvas.ctx.drawImage(this,0,0,config.video.width,config.video.height)
})
//删除图片
$view.find('.camera-canvas-group').on('click','.camera-canvas-item .canvas-item-del',function () {
//删除父节点元素
$(this).closest('.camera-canvas-item').remove()
})
//保存所有图片到本地
$view.find('#save').click(function () {
let fileName = getFileName();
let el_a = $('<a>');
layui.each($view.find('.camera-canvas-group .camera-canvas-item'),function (k,item) {
let t_filename = fileName+'_'+k+'.'+config.imgType;
// 解析,把照片上传到服务器。并回显到父级窗口
downLoad($(this).find('img').attr('src'),t_filename,config.imgType);
})
})
//空格按下拍照
$(document).keypress(function (e) {
e.keyCode===32&&$('#snap').trigger('click');
})
};
//tode
function getFileName() {
let date = new Date();
let fileName = ''+date.getFullYear()+(date.getMonth()<9?+'0':'')
+(date.getMonth()+1) +(date.getDate()<10?+'0':'')+date.getDate()
+date.getHours() +date.getMinutes()+(date.getSeconds()<10?'0':'')+date.getSeconds();
return fileName;
}
// 文件上传到ftp服务器
function downLoad(dataURL,fileName,fileType) {
var reader = new FileReader();
reader.readAsDataURL(createFile(dataURL));
reader.onload = function (e) {
// 转换成base64 进行父窗口的回显
/*console.log(window.parent.frames.length);*/
window.parent.frames[window.parent.frames.length-2].document.getElementById("shotPhoto").innerHTML=fileName;
window.parent.frames[window.parent.frames.length-2].document.getElementById("preShow").setAttribute("src",reader.result);
//进行文件上传到ftp服务器 ,将blob对象转换成file
let file = dataURLtoFile(dataURL,fileName);
var formData = new FormData();
formData.append("file",file);
$.ajax({
type:'post',
url:'你的上传接口地址',
data:formData,
dataType: "json",
processData: false,
contentType: false,
success:function (res) {
//上传失败
if(res.code > 0){
layer.msg('上传失败',{icon:2,time:1000});
}
// 上传成功
if(res.code == 0){
var objFile = JSON.parse(res.data);
window.parent.frames[window.parent.frames.length-2].document.getElementById("img_url").setAttribute("value",objFile['filePath']);
layer.msg('上传成功',{icon:1,time:1000});
setTimeout(function () {
$("#closeBtn").click();
},1000)
}
}
});
// 解析 BASE64文件内容 for IE,Edge
function createFile(urlData) {
var arr = urlData.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = window.atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
// base64 to file
function dataURLtoFile(dataurl, filename) {//将base64转换为文件
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type:mime});
}
function clearCanvas()
{
var c=document.getElementById("canvas");
var cxt=c.getContext("2d");
cxt.clearRect(0,0,c.width,c.height);
}
});
</script>
js文件:camvas.js
/*
实例化camvas配置参数
config = {
video:{width:Number(scale*4),height:Number(scale*3)},//视频比例4:3
canvasId:'canvas',//画布canvas节点ID
videoId:'v',//video节点ID
imgType:'png',//图片类型,/png|jpeg|bmp|gif/
quality:'1' //图片质量0-1之间
}
*/
window.URL = window.URL || window.webkitURL||window.mozURL || window.msURL;
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia
window.requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame
// Integrate navigator.getUserMedia & navigator.mediaDevices.getUserMedia
function getUserMedia (constraints, successCallback, errorCallback) {
if (!constraints || !successCallback || !errorCallback) {return}
if (navigator.mediaDevices) {
navigator.mediaDevices.getUserMedia(constraints).then(successCallback, errorCallback)
} else {
navigator.getUserMedia(constraints, successCallback, errorCallback)
}
}
//获取摄像头设备源
function getMediaStream() {
var exArray = []; //存储设备源ID
MediaStreamTrack.getSources(function (sourceInfos) {
for (var i = 0; i != sourceInfos.length; ++i) {
var sourceInfo = sourceInfos[i];
//这里会遍历audio,video,所以要加以区分
if (sourceInfo.kind === 'video') {
exArray.push(sourceInfo.id);
}
}
});
return exArray;
}
//用户手机端使用后置摄像头
function getMediaConfig() {
if (navigator.getUserMedia) {
if(/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)){
//手机端
return {
'video':
{'optional': [
{'sourceId': getMediaStream()[1] //0为前置摄像头,1为后置
}]
},
'audio':false
}
}else{
// 下面注释为强制使用后置摄像头
/*return { 'audio': false, 'video': { 'facingMode': { 'exact': "environment" } } }*/
return {'video':true,'audio':false}
}
}
else {
alert('Native device media streaming (getUserMedia) not supported in this browser.');
}
}
// The function takes a canvas context and a `drawFunc` function.
// `drawFunc` receives two parameters, the video and the time since
// the last time it was called.
function camvas(config) {
var self = this
self.convas = document.getElementById(config.canvasId)
self.ctx = self.convas.getContext('2d');
self.config = config
self.isStop = false;
//video节点ID
self.video = document.getElementById(self.config.videoId)
//video 显示尺寸
self.video.setAttribute('width', this.config.video.width)
self.video.setAttribute('height', this.config.video.height)
//视频流控制句柄
var mediaStreamTrack;
//对外开启视频方法
this.startCamera = function () {
// The callback happens when we are starting to stream the video.
getUserMedia(getMediaConfig(), function(stream) {
// Yay, now our webcam input is treated as a normal video and
// we can start having fun
try {
mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks().length==1 ?
stream.getTracks()[0]:stream.getTracks()[1];
if(self.video.mozSrcObject !== undefined){
//Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持
self.video.mozSrcObject = stream;
}else{
self.video.srcObject = stream;
}
} catch (error) {
self.video.src = window.URL && window.URL.createObjectURL(stream) || stream;
}
self.isStop = false;
self.video.play();
// Let's start drawing the canvas!
// self.recordVideo()
}, function(err){
alert(err);
})
}
//录像方法
this.recordVideo = function() {
var self = this
var last = Date.now()
var loop = function() {
// For some effects, you might want to know how much time is passed
// since the last frame; that's why we pass along a Delta time `dt`
// variable (expressed in milliseconds)
var dt = Date.now() - last
self.draw(self.video, dt)
last = Date.now()
requestAnimationFrame(loop)
}
requestAnimationFrame(loop)
}
//停止视频
this.stop = function () {
self.ctx.clearRect(0, 0, self.config.video.width,self.config.video.height);
mediaStreamTrack && mediaStreamTrack.stop();
self.isStop = true;
}
//拍照,base64/image/png
this.drawImage=function (callback) {
if(!self.isStop){
self.ctx.drawImage(self.video,0,0,self.config.video.width,self.config.video.height);
var base64URL = self.convas.toDataURL('image/'+self.config.imgType,self.config.quality);
callback&&callback(base64URL);
}
}
//录像数据帧
this.draw = function(video, dt) {
self.ctx.drawImage(video, 0, 0)
}
}
版权声明:本文标题:教你web如何调用摄像头拍照并上传到服务器(谷歌浏览器亲测可用!) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1727374982a1111600.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论