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">
<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)
惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。
一代天骄,成吉思汗,只识弯弓射大雕。
俱往矣,数风流人物,还看今朝