环境条件
- 具有内置摄像头 或 外设摄像头
- Vue + Element + axios(环境不同可自行修改,本文主要为逻辑,除展示的上传控件其他与js基本无异)
全文
<template>
<div id="all">
<el-upload
:headers="{token:userinfo.token}"
class="upload-demo"
list-type="picture-card"
:multiple="false"
:limit="5"
:action="uploadimgUrl"
:on-success="uploadPhoto"
:on-remove="handleRemovePhoto"
:file-list="fileListimg"
multiple>
<i class="el-icon-plus"></i>
</el-upload>
<div class="gp">
<video ref="gpp2"></video>
</div>
<div class="footer" style="display: flex;justify-content: space-between;margin-top: 10px">
<el-button type="primary" @click="takeGpp">抓 拍 上 传</el-button>
</div>
<canvas ref="canvas" v-show="false" height="960" width="1600"></canvas>
</div>
</template>
<script>
import request from "@/utils/request";
export default {
name: "demo",
data(){
let url = process.env.VUE_APP_BASE_API;
let baseUrl = this.$baseUrl;
let userinfo = JSON.parse(sessionStorage.getItem('userinfo'))
return{
fileListimg:[],
fileliststr:"",
video:{},
baseUrl,
userinfo,
uploadimgUrl:url+"/upload_images",
}
},
mounted() {
this.initGpp();
},
methods:{
openGpp() {
// this.dialogFormVisible = true;
setTimeout(() => {
this.initGpp();
}, 100)
},
initGpp() {
this.video = this.$refs.gpp2;
console.log(this.video)
// this.video = document.getElementById('video')
//兼容性写法,判断getUserMedia方法是否存在
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || window.getUserMedia;
if (navigator.getUserMedia) {
//调用用户媒体设备, 访问摄像头
this.getUserMedia({video: {width: 1600, height: 960}}, this.success, this.error);
} else {
alert('不支持访问用户媒体');
}
},
getUserMedia(constraints, success, error) {
if (navigator.mediaDevices.getUserMedia) {
//最新的标准API
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit核心浏览器
navigator.webkitGetUserMedia(constraints, success, error)
} else if (navigator.mozGetUserMedia) {
//firfox浏览器
navigator.mozGetUserMedia(constraints, success, error);
} else if (navigator.getUserMedia) {
//旧版API
navigator.getUserMedia(constraints, success, error);
}
},
// 调用成功的方法
success(stream) {
//兼容webkit核心浏览器
let CompatibleURL = window.URL || window.webkitURL;
//将视频流设置为video元素的源
// console.log(stream);
//video.src = CompatibleURL.createObjectURL(stream);
//将摄像头拍摄的视频赋值给viedeo的srcObject属性
//src是视频文件,srcObject是实时流
//摄像头是实时流
this.video.srcObject = stream;
//并播放
this.video.play();
},
// 调用失败的方法
error(error) {
console.log(`访问用户媒体设备失败${error.name}, ${error.message}`);
},
takeGpp() {
if(this.fileListimg.length>=5){
this.$message({
type: 'error',
message: "图片最多允许五张"
});
return
}
let canvas = this.$refs.canvas;
var context = canvas.getContext("2d");
//将视频当前的页面转换为图片,显示到画板中
context.drawImage(this.video, 0, 0,);
//把canvas图像转为img图片
var src = canvas.toDataURL("image/jpeg");
// console.log(src)
let file = this.base64ToFile(src);
let time = new Date().getTime();
// this.downloadFile(file, `${time}.png`)
let type = file.type.split('/');
let renameFile = new File([file],time+".png",{type:"image/png"})
let formData = new FormData();
formData.append('file',renameFile)
request({
url:"/upload_images",
method:"post",
data:formData
}).then(res => {
console.log(res)
this.$message({
type: 'success',
message: "上传成功"
});
this.fileListimg.push({
status:"success",
name:time+".png",
uid:time,
url:this.baseUrl + res.data.imgurl,
response:res
})
})
},
base64ToFile(base64Data, fileName) {
let arr = base64Data.split(','),
fileType = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
l = bstr.length,
u8Arr = new Uint8Array(l);
while (l--) {
u8Arr[l] = bstr.charCodeAt(l);
}
return new File([u8Arr], fileName, {type: fileType});
},
downloadFile(content, filename) {
var a = document.createElement('a')
var blob = new Blob([content])
var url = window.URL.createObjectURL(blob)
a.href = url
a.download = filename
a.click()
console.log(url)
window.URL.revokeObjectURL(url)
},
handleRemovePhoto(file, fileList) {
console.log(file, fileList);
this.fileListimg = fileList;
// let index=null;
// fileList.map((item,i)=> {
// if(file.uid === item.uid){
// index = i;
// }
// })
// fileList.splice(index,1);
},
uploadPhoto(response, file, fileList) {
file.url = this.baseUrl+response.data.imgurl;
console.log(fileList)
this.fileliststr = JSON.stringify(fileList)
if(response.code == 0){
// this.info.fujian = response.data.imgurl;
// this.info.fujian_name = response.file_name;
}else{
// this.fileList = [];
this.$message({
type: 'error',
message: response.msg
});
}
},
}
}
</script>
<style lang="scss" scoped>
#all {
min-height: 100vh;
background-color: #F5F7FD;
padding: 10px 30px 60px;
.gp {
margin-top: 40px;
height: 480px;
width: 800px;
video {
height: 100%;
width: 100%;
}
}
}
</style>
- 静态效果
![演示效果](https://i-blog.csdnimg.cn/blog_migrate/c8e65dcffc9fb44d5aa5874b741b4145.png)
- 拍照后效果(上展示组件为Element的upload的
list-type="picture-card"
模式)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8f293766939186ae5ac867bb60feedf8.png)
步骤解析
- 需要一个video控件和一个canvas控件(可隐藏)
<div class="gp">
<video ref="gpp2"></video>
</div>
<div class="footer" style="display: flex;justify-content: space-between;margin-top: 10px">
<el-button type="primary" @click="takeGpp">抓 拍 上 传</el-button>
</div>
<canvas ref="canvas" v-show="false" height="960" width="1600"></canvas>
这里我使用了一个来自Element的upload组件来展示,尽管你可能并不需要
<el-upload
:headers="{token:userinfo.token}"
class="upload-demo"
list-type="picture-card"
:multiple="false"
:limit="5"
:action="uploadimgUrl"
:on-success="uploadPhoto"
:on-remove="handleRemovePhoto"
:file-list="fileListimg"
multiple>
<i class="el-icon-plus"></i>
</el-upload>
handleRemovePhoto(file, fileList) {
console.log(file, fileList);
this.fileListimg = fileList;
},
uploadPhoto(response, file, fileList) {
if(response.code == 0){
file.url = this.baseUrl+response.data.imgurl;
this.fileliststr = JSON.stringify(fileList)
}else{
this.$message({
type: 'error',
message: response.msg
});
}
},
- 准备数据
- 除了
fileListimg
和video
其他都不是必须的,根据自行需要准备
data(){
let url = process.env.VUE_APP_BASE_API;
let baseUrl = this.$baseUrl;
let userinfo = JSON.parse(sessionStorage.getItem('userinfo'))
return{
fileListimg:[],
fileliststr:"",
video:{},
baseUrl,
userinfo,
uploadimgUrl:url+"/upload_images",
}
},
- 初始化video和摄像头
- 这一步网上的教程已经很多了
- 在页面渲染完毕后调用
initGpp()
initGpp() {
this.video = this.$refs.gpp2;
console.log(this.video)
// this.video = document.getElementById('video')
//兼容性写法,判断getUserMedia方法是否存在
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || window.getUserMedia;
if (navigator.getUserMedia) {
//调用用户媒体设备, 访问摄像头
this.getUserMedia({video: {width: 1600, height: 960}}, this.success, this.error);
} else {
alert('不支持访问用户媒体');
}
},
getUserMedia(constraints, success, error) {
if (navigator.mediaDevices.getUserMedia) {
//最新的标准API
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit核心浏览器
navigator.webkitGetUserMedia(constraints, success, error)
} else if (navigator.mozGetUserMedia) {
//firfox浏览器
navigator.mozGetUserMedia(constraints, success, error);
} else if (navigator.getUserMedia) {
//旧版API
navigator.getUserMedia(constraints, success, error);
}
},
// 调用成功的方法
success(stream) {
//兼容webkit核心浏览器
let CompatibleURL = window.URL || window.webkitURL;
//将视频流设置为video元素的源
// console.log(stream);
//video.src = CompatibleURL.createObjectURL(stream);
//将摄像头拍摄的视频赋值给viedeo的srcObject属性
//src是视频文件,srcObject是实时流
//摄像头是实时流
this.video.srcObject = stream;
//并播放
this.video.play();
},
// 调用失败的方法
error(error) {
console.log(`访问用户媒体设备失败${error.name}, ${error.message}`);
}
- 拍照
takeGpp() {
// 在这里可以做一些前期校验
if(this.fileListimg.length>=5){
this.$message({
type: 'error',
message: "图片最多允许五张"
});
return
}
let canvas = this.$refs.canvas;
var context = canvas.getContext("2d");
//将视频当前的页面转换为图片,显示到画板中
context.drawImage(this.video, 0, 0,);
//把canvas图像转为img图片
var src = canvas.toDataURL("image/jpeg");
let file = this.base64ToFile(src);
// 获取时间,准备当做文件名
let time = new Date().getTime();
// 如果你需要是下载,那么请将下一行解除注释,并将下一行以下的内容全部删除
// this.downloadFile(file, `${time}.png`)
// 重命名文件并改变格式
let type = file.type.split('/');
let renameFile = new File([file],time+".png",{type:"image/png"})
let formData = new FormData();
formData.append('file',renameFile)
// 上传 request封装方法 继续阅读
request({
url:"/upload_images",
method:"post",
data:formData
}).then(res => {
console.log(res)
this.$message({
type: 'success',
message: "上传成功"
});
// 这里是为了使用Element的upload组件展示而做的,向该数组添加相似结构数据
// 请根据自行需要
this.fileListimg.push({
status:"success",
name:time+".png",
uid:time,
url:this.baseUrl + res.data.imgurl,
response:res
})
})
},
base64ToFile(base64Data, fileName) {
let arr = base64Data.split(','),
fileType = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
l = bstr.length,
u8Arr = new Uint8Array(l);
while (l--) {
u8Arr[l] = bstr.charCodeAt(l);
}
return new File([u8Arr], fileName, {type: fileType});
},
// 下载会用到该方法
downloadFile(content, filename) {
var a = document.createElement('a')
var blob = new Blob([content])
var url = window.URL.createObjectURL(blob)
a.href = url
a.download = filename
a.click()
console.log(url)
window.URL.revokeObjectURL(url)
}
- 大功告成
request.js
- 参考了
Vue-admin-template
的封装设计
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
timeout: 10000 // request timeout
})
service.interceptors.request.use(
config => {
let token = sessionStorage.getItem("token")
if(token){
config.headers['token'] = token;
}
return config
},
error => {
console.log(error)
return Promise.reject(error)
}
)
service.interceptors.response.use(
response => {
const res = response.data
if (res.code === 3) {
// 未登录
Message.error("未登录")
store.dispatch('user/logout').then(() => {
location.reload()
})
return Promise.reject(new Error(res.message || 'Error'))
}
return res
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service