JavaScript实现调用摄像头完成拍照取图 重命名并下载或上传

环境条件

  • 具有内置摄像头 或 外设摄像头
  • 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>

  1. 静态效果
    演示效果
  2. 拍照后效果(上展示组件为Element的upload的list-type="picture-card"模式)

在这里插入图片描述

步骤解析

  1. 需要一个video控件和一个canvas控件(可隐藏)
  • 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
        });
      }
    },
  1. 准备数据
  • 除了fileListimgvideo其他都不是必须的,根据自行需要准备
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",
    }
  },
  1. 初始化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}`);
    }
  1. 拍照
  • 这一步是重点
  • 上传下载在这一步决定
  • 具体看注释
	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)
    }
  1. 大功告成

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值