VUE 调用电脑摄像头进行拍照并上传到服务器保存,附前后端代码

前端代码

<template>

  <div id="picUpload">
    <div>
      <el-form ref="uploadForm" :model="uploadForm" :rules="rules" label-width="120px">
        <el-row>
          <el-form-item label="文件来源系统" prop="fileSource">
            <el-select style="width: 100%" v-model="uploadForm.fileSource" placeholder="文件来源系统" clearable>
              <el-option v-for="(val, key) in fileSource" :key="key" :label="val.dictLabel" :value="val.dictValue" />
            </el-select>
          </el-form-item>
        </el-row>
        <el-row>
          <el-form-item label="单据业务类型" prop="orderType">
            <el-select style="width: 100%" v-model="uploadForm.orderType" placeholder="单据业务类型" clearable>
              <el-option v-for="(val, key) in orderType" :key="key" :label="val.dictLabel" :value="val.dictValue" />
            </el-select>
          </el-form-item>
        </el-row>
        <el-row>
          <el-form-item label="WMS出入库单" prop="code">
            <el-input v-model="uploadForm.code" placeholder="WMS出入库单" @keyup.enter.native="checkOrder"
              @input="handleInputChange" />
          </el-form-item>
        </el-row>
        <el-row>
          <el-form-item label="文件名称" prop="filename">
            <el-input v-model="uploadForm.filename" :disabled="isfileName" ref="filename" @keyup.enter.native="capture"
              placeholder="①不填写文件名默认使用单号+数字自动生成文件名,填写后以填写的为主;②在此文本框按下回车键,即可实现快捷拍照" />
          </el-form-item>
        </el-row>
        <el-row>
          <el-col :span="1.5">
            <el-button class="large-button" type="primary" plain icon="el-icon-upload2" :loading="buttenloading"
              @click="capture()" :disabled="isButtonDisabled">拍照</el-button>
            <el-button size="large" type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
          </el-col>
          <!-- <button @click="capture">拍照</button> -->
        </el-row>
      </el-form>

    </div>
    <div style="display: flex;align-items: stretch; ">
      <div style="flex: 1;display: flex; flex-direction: column; width: 80%;">
        <video id="video" width="1000" height="750" autoplay></video>
        <canvas id="canvas" width="1000" height="750" style="display:none;"></canvas>
      </div>
      <div style="flex: 1;display: flex; flex-direction: column; width: 200px">
        <el-table stripe border :height="130" cell-class-name="" v-loading="loading" :data="roleList">
          <el-table-column label="操作" width="151" align="center" class-name="small-padding fixed-width">
            <template slot-scope="scope">
              &nbsp;<el-button type="text" icon="el-icon-view" @click="handCheck(scope.row, 'view')">文件预览</el-button>
              <el-button type="text" icon="el-icon-view" @click="handleDelete(scope.row)">文件删除</el-button>
            </template>
          </el-table-column>
          <el-table-column label="文件编码" width="100" prop="code" :show-overflow-tooltip="true" />
          <el-table-column label="文件名称" prop="name" :show-overflow-tooltip="true" />
          <el-table-column label="单据类型" prop="orderType" :show-overflow-tooltip="true">
            <template slot-scope="scope">
              <span>{{ formaType(scope.row.orderType) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="文件来源" prop="fileSource" :show-overflow-tooltip="true" />

        </el-table>
      </div>
    </div>
    <el-dialog :visible.sync="imgViewIsShow" width="1042px" :title=title>
      <div class="demo-image__preview" style="width: 1000px">
        <el-image :style="{ zIndex: 99999, width: '1000px' }" :src="imgView" :preview-src-list="imgViewList">
          <div slot="error" class="image-slot">
            <i class="el-icon-picture-outline"></i>
          </div>
        </el-image>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import {
  list,
  deleteDoc,
  uploadDocFileForOrder,
  checkOrder,
} from "@/api/orderImgManage";
export default {
  name: "PicUpload",
  data() {
    return {
      title: "图片预览",
      // 遮罩层
      loading: false,
      // 按钮处理中
      buttenloading: false,
      isButtonDisabled: true, // 初始时按钮为禁用状态
      isfileName: true, // 初始文件名文本框为禁用状态
      // 图片预览框
      imgViewIsShow: false,
      imgViewList: [],
      imgView: "",
      picnum: 0,
      roleList: [],
      orderType: [],
      fileSource: [],
      uploadForm: {
        code: undefined,
        orderType: undefined,
        createBy: undefined,
        createTime: undefined,
        fileSource: undefined,
        filename: undefined,
        fileList: undefined,
      },
      // 表单校验
      rules: {
        orderType: [
          { required: true, message: "请选择单据业务类型", trigger: "blur" },
        ],
        code: [{ required: true, message: "请输入单号", trigger: "blur" }],
        fileSource: [
          { required: true, message: "请选择文件类型", trigger: "blur" },
        ],
      },
    }
  },

  created() {
    // 获取数据字典  单据业务类型
    this.getDicts("order_type").then((response) => {
      this.orderType = response.data;
    });
    // 获取数据字典 文件来源系统
    this.getDicts("file_source").then((response) => {
      this.fileSource = response.data;
    });
    // 来源系统默认选择WMS
    this.uploadForm.fileSource = 'WMS'
  },
  methods: {
    formaType(value) {
      return this.selectDictLabel(this.orderType, value);
    },
    handleInputChange() {
      // 检查uploadForm.code是否为空,相应地更新isButtonDisabled变量
      if (!this.uploadForm.code.trim()) {
        this.isButtonDisabled = true;
        this.isfileName = true;
      }
    },
    checkOrder() {
      if (
        this.uploadForm.fileSource == undefined ||
        this.uploadForm.fileSource.length == 0
      ) {
        this.$message.error("请选择文件来源");
        return false;
      }
      if (
        this.uploadForm.orderType == undefined ||
        this.uploadForm.orderType.length == 0
      ) {
        this.$message.error("请选择单据业务类型");
        return false;
      }
      if (
        this.uploadForm.code == undefined ||
        this.uploadForm.code.length == 0
      ) {
        this.$message.error("请输入单号");
        return false;
      }
      if (this.uploadForm.code != undefined && this.uploadForm.code.trim().length > 0) {
        checkOrder(this.uploadForm).then((response) => {
          if (response.code == 200) {
            this.isButtonDisabled = false;
            this.picnum = 0;
            this.isfileName = false;
            this.$nextTick(() => {
              this.$refs.filename.focus();
              this.getList();
            });
          } else {
            this.$message.error(response.msg);
            return false;
          }
        })

      }

      // this.$refs.filename.focus();
    },
    async fnUploadDocFileForOrder(data) {
      return uploadDocFileForOrder(data);
    },
    handCheck(data, type) {
      var data = data;
      if (type == "view") {
        if (["jpg", "png"].includes(data.fileType)) {
          this.title = data.name + '-文件预览'
          this.imgViewIsShow = true;
          this.imgView = data.fileUrl;
          this.imgViewList.push(data.fileUrl);
        } else {
          this.$message.error("非图片格式不能预览");
        }
      }
    },
    handleDelete(row) {
      const ids = row.id || this.ids;
      this.$confirm("是否确认删除数据项?", "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(function () {
          return deleteDoc(ids);
        })
        .then(() => {
          this.getList();
          this.msgSuccess("删除成功");
        }).catch(() => {
          console.log('取消对话框');
        });
    },
    handleQuery() {
      if (
        this.uploadForm.fileSource == undefined ||
        this.uploadForm.fileSource.length == 0
      ) {
        this.$message.error("请选择文件来源");
        return false;
      }
      if (
        this.uploadForm.orderType == undefined ||
        this.uploadForm.orderType.length == 0
      ) {
        this.$message.error("请选择单据业务类型");
        return false;
      }
      if (
        this.uploadForm.code == undefined ||
        this.uploadForm.code.length == 0
      ) {
        this.$message.error("请输入单号");
        return false;
      }
      this.getList();
    },
    getList() {
      this.loading = true;
      list(this.addDateRange(this.uploadForm, this.dateRange)).then(
        (response) => {
          this.roleList = response.rows;
          this.loading = false;
        }
      );
    },
    async capture() {
      if (
        this.uploadForm.fileSource == undefined ||
        this.uploadForm.fileSource.length == 0
      ) {
        this.$message.error("请选择文件来源");
        return false;
      }
      if (
        this.uploadForm.orderType == undefined ||
        this.uploadForm.orderType.length == 0
      ) {
        this.$message.error("请选择单据业务类型");
        return false;
      }
      if (
        this.uploadForm.code == undefined ||
        this.uploadForm.code.length == 0
      ) {
        this.$message.error("请输入单号");
        return false;
      }

      this.buttenloading = true;
      const video = document.getElementById('video');
      const canvas = document.getElementById('canvas');
      const context = canvas.getContext('2d');
      context.drawImage(video, 0, 0, 1000, 750);
      // 如果有自定义文件名称,优先使用自定义的文件名称
      let filenames = ''
      if (this.uploadForm.filename != undefined && this.uploadForm.filename != '') {
        filenames = this.uploadForm.filename + '.png'
      } else {
        filenames = this.uploadForm.code + '_' + this.picnum + '.png'
      }
      console.log(filenames);
      // 将canvas内容转换为Blob对象
      canvas.toBlob(async blob => {
        // 创建FormData
        const fd = new FormData();
        fd.append('file', blob, filenames);
        this.uploadForm.fileList = fd;
        let cc = await this.fnUploadDocFileForOrder(this.uploadForm);
        console.log(cc);
        if (200 != cc.code) {
          this.$message.error(cc.msg);
          return false;
        }
        // 调用后端接口上传照片
        // axios.post('http://localhost:8421/upload_one', formData, {
        //   headers: {
        //     'Content-Type': 'multipart/form-data'
        //   }
        // }).then(response => {
        //   console.log('上传成功', response);
        // }).catch(error => {
        //   console.error('上传失败', error);
        // });

        this.handleQuery()
      }, 'image/png');
      this.buttenloading = false;
      this.picnum += 1;
    }
  },
  mounted() {
    // 获取摄像头视频流
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
        const video = document.getElementById('video');
        video.srcObject = stream;
        video.play();
      });
    }
  }
}
</script>
<style>
.large-button {
  padding: 25px 50px;
  /* 调整内边距来增加按钮的大小 */
  font-size: 40px;
  /* 调整字体大小 */
}
</style>

JS接口,当然,如果只是demo 可以直接使用axios调用接口上传

import request from '@/utils/request'

// 查询文件记录列表
export function list(query) {
    return request({
        url: '/demo/docfile/pageList',
        method: 'get',
        params: query
    })
}

// 删除文件记录
export function deleteDoc(ids) {
    return request({
        url: '/demo/docfile/delete/' + ids,
        method: 'delete'
    })
}


export function uploadDocFileForOrder(data) {
    return request({
        url: `/demo/docfile/uploadDocFileForOrder?code=${data.code}&orderType=${data.orderType}&fileSource=${data.fileSource}`,
        method: 'post',
        data: data.fileList
    })
}

export function checkOrder(data) {
    return request({
        url: `/demo/docfile/checkOrder?code=${data.code}&orderType=${data.orderType}&fileSource=${data.fileSource}`,
        method: 'post',
        data: data
    })
}

后端代码,只提供java controller的相关文件上传的接口

    @Log(title = "上传单个单号文件", businessType = BusinessType.IMPORT)
    @RequestMapping(value = "uploadDocFileForOrder", method = RequestMethod.POST)
    @PreAuthorize()
    @ResponseBody
    public R uploadDocFileForOrder(@RequestParam(value = "file") MultipartFile[] files, @RequestParam Map<String, String> params, HttpServletRequest requestNew) {
        log.info("批量上传文件开始==============");
        String orderType = params.get("orderType");
        String orderCode = params.get("code");
        String fileSource = params.get("fileSource");
        if (files == null || files.length == 0) {
            return R.fail("请先选择文件");
        }
        long start = System.currentTimeMillis();
        try {
            if (StringUtils.isEmpty(orderType)) {
                return R.fail("单据类型必须填写");
            }
            if (StringUtils.isEmpty(fileSource)) {
                return R.fail("单据来源必须填写");
            }
            if (StringUtils.isEmpty(orderCode)) {
                return R.fail("单据编码必须填写");
            }
            SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
            String dirName = sf.format(new Date());
            String filePath = docfilepath + dirName;//excel生成的文件路径
            File fileNew = new File(filePath);
            if (!fileNew.exists()) {//判断目录是否存在
                fileNew.mkdirs();
            }
            String userName = "";
//            String userName = requestNew.getHeader("username");
            LoginUser loginUser = tokenService.getLoginUser();
            if (null != loginUser) {
                userName = loginUser.getUsername();
            }

            System.out.println(loginUser);
            for (MultipartFile mf : files) {
                R r=dfSvc.addFile(mf, mf.getOriginalFilename(), orderType, orderCode, fileSource, filePath, userName, false);
                if (r.hasError()){
                    return r;
                }
            }

            log.info("批量上传文件结束==============总耗时:" + (System.currentTimeMillis() - start) + " ms");
            return R.ok("上传任务请求成功,总请求文件:" + files.length + ",请稍后查看任务执行结果");
        } catch (Exception e) {
            log.error("批量上传到服务器出错!", e);
            return R.fail("上传失败:" + e.toString());
        }
    }
	
	
	public R addFile(MultipartFile singleFile, String originalFilename, String orderType, String orderCode, String fileSource, String filePath, String userName, boolean isBatchUpload) {
        String filePathNew = "";
        R r = R.ok();
        try {
            log.info("上传单个文件开始;文件名:" + originalFilename);
            long localStartTime = System.currentTimeMillis();
            if (isBatchUpload) {
                String[] fileNameList = originalFilename.split("\\.");
                // 认为第一个.前的是文件名
                if (fileNameList.length > 0) {
                    String fileName = fileNameList[0];
                    String[] orderCodes = fileName.split("_");
                    // 通过文件名 拆单号
                    orderCode = orderCodes[0];
                }
            }
            if (StringUtils.isEmpty(orderCode)) {
                r = R.fail("单据号,不能为空");
                return r;
            }
            if ("RECEIPT".equals(orderType)) {
                // 判断是否存在单号
                if (!shSvc.getReceiptByWms(orderCode)) {
                    r = R.fail(String.format("入库单号【%s】不存在", orderCode));
                    return r;

                }
            } else if ("SHIPMENT".equals(orderType)) {
                // 判断是否存在单号
                if (!shSvc.getShipmentByWms(orderCode)) {
                    r = R.fail(String.format("出库单号【%s】不存在", orderCode));
                    return r;
                }
            } else {
                r = R.fail(String.format("单据类型【%s】不正确", orderType));
                return r;
            }


            //生成文件名称已uuid命名
            String uuid = originalFilename + UUID.randomUUID().toString().replaceAll("-", "");
            String[] fileNameArr = originalFilename.split("\\.");
            String fileName = uuid + "." + fileNameArr[fileNameArr.length - 1];
            filePathNew = filePath + "/" + fileName;
            File newFile = new File(filePathNew);
            singleFile.transferTo(newFile);

            // 文件上传完成之后,保存系统对照数据
            DocFile df = new DocFile();
            df.setCode(orderCode);
            df.setName(originalFilename);
            df.setOrderType(orderType);
            df.setServersFileName(fileName);
            df.setServersFilePath(filePathNew);
            df.setFileSource(fileSource);
            df.setCreateBy(userName);
            df.setFileType(fileNameArr[fileNameArr.length - 1]);
            df.setFileUrl(filePathNew.replace(docfilepath, docfileUrl));
            R res = addDf(df);

            if (res.hasError()) {
                //删除服务器文件
                delMethod(filePathNew);
                r = R.fail("上传失败:" + res.getMsg());
                return r;
            }
            String loga = "上传单个文件结束,【" + originalFilename + "】服务器所耗时间:" + (System.currentTimeMillis() - localStartTime) + "ms;(非网络耗时)";
            log.info(loga);
            //修改订单为已上传单据
            wmsRhSvc.updateUploadStatus(orderCode, userName);
            r = R.ok(null, loga);
            return r;
        } catch (Exception e) {
            log.error(e.toString());
            //删除服务器文件
            delMethod(filePathNew);
            r = R.fail("上传失败:" + e.toString());
            return r;
        } finally {
            UploadFileLogDocment doc = new UploadFileLogDocment();
            doc.setCode(orderCode);
            doc.setName(originalFilename);
            doc.setOrderType(orderType);
            doc.setMsgResult(r.getMsg());
            doc.setStatus(r.hasError() ? 1 : 0);
            doc.setMsgType(isBatchUpload ? "批量上传" : "按单上传");
            LocalDateTime date = LocalDateTime.parse(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), dtf);
            doc.setCreated(date);
            doc.setFileSize(singleFile.getSize() / 1024);
            //获取用户
            doc.setCreatedBy(userName);
            // 上传日志
            sendUploadFileLog(doc);
        }

    }

python的后端接收文件的代码

# -*- ecoding: utf-8 -*-
# @ModuleName: test002
# 当你要使用这份文件时,
# 代表你已经完全理解文件内容的含义,
# 并愿意为使用此文件产生的一切后果,付全部责任
# @Funcation: 
# @Author: darling
# @Time: 2024-06-17 14:21
from flask_cors import CORS
from flask import Flask, request
import os
from loguru import logger

app = Flask(__name__)
CORS(app)

@app.route('/upload_one', methods=['POST'])
def upload_one():
    '''
    前端上传,批量选择后,前端循环上传,后端单个接收
    :return:
    '''
    file = request.files['file']  # 获取上传的文件

    if file:
        logger.info('获取到文件{}', file.filename)
        file.save(os.path.join('files', file.filename))  # 保存文件到当前目录
        logger.info('保存结束{}', file.filename)
        return '文件上传成功!'
    else:
        return '文件上传失败!'


@app.route('/upload_batch', methods=['POST'])
def upload_batch():
    '''
    前端上传,批量选择后一次性上传,后端循环保存
    :return:
    '''
    files = request.files.getlist('files')  # 获取上传的文件列表

    if files:
        for file in files:
            logger.info('获取到文件{}', file.filename)
            file.save(os.path.join('files', file.filename))  # 保存文件到当前目录
            logger.info('保存结束{}', file.filename)
        return '文件上传成功!'
    else:
        return '文件上传失败!'


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8421)

posted @ 2024-06-28 18:05  darling331  阅读(4)  评论(0编辑  收藏  举报