|
@@ -1,83 +1,72 @@
|
|
|
<template>
|
|
|
- <div style='height: 100%;'>
|
|
|
- <div id='import-header'>
|
|
|
- <img src='../assets/images/nav_images.png' alt=''>照片批量上传工具
|
|
|
- <button id='exit-btn' @click='logout' title="退出"></button>
|
|
|
- </div>
|
|
|
- <div id='import-body'>
|
|
|
- <div id='import-div'>
|
|
|
- <a href='javascript:;' class='file'>
|
|
|
- <!-- webkitdirectory-->
|
|
|
- <button id='import-btn' @click="importPhotos">
|
|
|
- 上传照片
|
|
|
- </button>
|
|
|
- </a>
|
|
|
- <div id='progressDiv'>
|
|
|
- <span>
|
|
|
- 成功:
|
|
|
- <span style="color:green;">{{successNum}}</span>/{{allNum}}
|
|
|
- </span>
|
|
|
- <span>
|
|
|
- 失败:
|
|
|
- <span style="color:red;">{{errorNum}}</span>/{{allNum}}
|
|
|
- </span>
|
|
|
- <span v-show="completeShow" style="color: green;font-weight: bold;">全部处理完成</span>
|
|
|
- <span v-show="!completeShow&&(successNum>0||errorNum>0)" style="color: red;font-weight: bold;">处理中...</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div id='progress-div'>
|
|
|
- <div id='console-panel'>
|
|
|
- <div v-for='msgInfo in returnMsgList' :key="msgInfo.fileName">
|
|
|
- <p class='console-line' v-if='msgInfo.success'>
|
|
|
- <span>
|
|
|
- <img src='../assets/images/icon_sucess.png'>
|
|
|
- </span>
|
|
|
- <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
|
|
|
- </p>
|
|
|
- <p class='console-line' v-if='!msgInfo.success'>
|
|
|
- <span>
|
|
|
- <img src='../assets/images/icon_failed.png'>
|
|
|
- </span>
|
|
|
- <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div style='height: 100%;'>
|
|
|
+ <div id='import-header'>
|
|
|
+ <img src='../assets/images/nav_images.png' alt=''>照片批量上传工具
|
|
|
+ <button id='exit-btn' @click='logout' title="退出"></button>
|
|
|
+ </div>
|
|
|
+ <div id='import-body'>
|
|
|
+ <div id='import-div'>
|
|
|
+ <a href='javascript:;' class='file'>
|
|
|
+ <!-- webkitdirectory-->
|
|
|
+ <button id='import-btn' @click="importPhotos">
|
|
|
+ 上传照片
|
|
|
+ </button>
|
|
|
+ </a>
|
|
|
+ <div id='progressDiv'>
|
|
|
+ <span>
|
|
|
+ 成功:
|
|
|
+ <span style="color:green;">{{successNum}}</span>/{{allNum}}
|
|
|
+ </span>
|
|
|
+ <span>
|
|
|
+ 失败:
|
|
|
+ <span style="color:red;">{{errorNum}}</span>/{{allNum}}
|
|
|
+ </span>
|
|
|
+ <span v-show="completeShow" style="color: green;font-weight: bold;">全部处理完成</span>
|
|
|
+ <span v-show="!completeShow&&(successNum>0||errorNum>0)" style="color: red;font-weight: bold;">处理中...</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div id='progress-div'>
|
|
|
+ <div id='console-panel'>
|
|
|
+ <div v-for='msgInfo in returnMsgList' :key="msgInfo.fileName">
|
|
|
+ <p class='console-line' v-if='msgInfo.success'>
|
|
|
+ <span>
|
|
|
+ <img src='../assets/images/icon_sucess.png'>
|
|
|
+ </span>
|
|
|
+ <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
|
|
|
+ </p>
|
|
|
+ <p class='console-line' v-if='!msgInfo.success'>
|
|
|
+ <span>
|
|
|
+ <img src='../assets/images/icon_failed.png'>
|
|
|
+ </span>
|
|
|
+ <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
const CryptoJS = require("crypto-js");
|
|
|
const Base64 = require("js-base64").Base64;
|
|
|
-const fs = nodeRequire("fs");
|
|
|
+const fs = window.nodeRequire("fs");
|
|
|
+const path = window.nodeRequire("path");
|
|
|
const async = require("async");
|
|
|
-import parallelLimit from "async/parallelLimit";
|
|
|
|
|
|
function isImageFile(fileName) {
|
|
|
- let lowercaseName = fileName.toLowerCase();
|
|
|
- if (
|
|
|
+ const lowercaseName = fileName.toLowerCase();
|
|
|
+ return (
|
|
|
lowercaseName.endsWith(".jpg") ||
|
|
|
lowercaseName.endsWith(".jpeg") ||
|
|
|
lowercaseName.endsWith(".png")
|
|
|
- ) {
|
|
|
- return true;
|
|
|
- } else {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
-//读取文件
|
|
|
+//读取文件,返回网站路径. 只扫描当前文件夹,不递归扫描
|
|
|
function readImageFiles(folderPath) {
|
|
|
const files = fs.readdirSync(folderPath);
|
|
|
- return files.filter(isImageFile).map(fileName => {
|
|
|
- const fullPath = folderPath + "/" + fileName;
|
|
|
- //只扫描当前文件夹,不递归扫描
|
|
|
- var student = {};
|
|
|
- student.fileName = fileName;
|
|
|
- student.path = fullPath;
|
|
|
- return student;
|
|
|
- });
|
|
|
+ return files.filter(isImageFile).map(fileName => folderPath + "/" + fileName);
|
|
|
}
|
|
|
|
|
|
const CONCURRENCY = 2; //同时处理的照片数量
|
|
@@ -86,8 +75,6 @@ export default {
|
|
|
data() {
|
|
|
return {
|
|
|
photoList: [], //总的照片队列
|
|
|
- photoLine: [], //正在处理的照片队列
|
|
|
-
|
|
|
returnMsgList: [], //返回信息
|
|
|
successNum: 0, //成功数量
|
|
|
errorNum: 0, //失败数量
|
|
@@ -98,7 +85,6 @@ export default {
|
|
|
methods: {
|
|
|
init() {
|
|
|
this.photoList = [];
|
|
|
- this.photoLine = [];
|
|
|
this.returnMsgList = [];
|
|
|
this.successNum = 0;
|
|
|
this.errorNum = 0;
|
|
@@ -107,7 +93,7 @@ export default {
|
|
|
},
|
|
|
importPhotos() {
|
|
|
this.init();
|
|
|
- var electron = nodeRequire("electron");
|
|
|
+ var electron = window.nodeRequire("electron");
|
|
|
let dialog = electron.remote.dialog;
|
|
|
dialog.showOpenDialog(
|
|
|
{
|
|
@@ -124,39 +110,41 @@ export default {
|
|
|
processQueue(folderPath) {
|
|
|
this.photoList = readImageFiles(folderPath);
|
|
|
this.allNum = this.photoList.length;
|
|
|
- var _this = this;
|
|
|
|
|
|
let taskQueue = [];
|
|
|
- for (const photo of this.photoList) {
|
|
|
- taskQueue.push(this.processStudentPhoto.bind(this, photo));
|
|
|
+ for (const studentPhotoPath of this.photoList) {
|
|
|
+ taskQueue.push(this.processStudentPhoto.bind(this, studentPhotoPath));
|
|
|
}
|
|
|
|
|
|
- async.parallelLimit(
|
|
|
- taskQueue,
|
|
|
- CONCURRENCY,
|
|
|
- // optional callback
|
|
|
- function(err, results) {
|
|
|
- if (err) {
|
|
|
- alert(err);
|
|
|
- console.log(err);
|
|
|
- } else {
|
|
|
- console.log("photoList处理完毕");
|
|
|
- _this.completeShow = true;
|
|
|
- }
|
|
|
+ // 并发处理请求,可在控制台查看请求峰值
|
|
|
+ async.parallelLimit(taskQueue, CONCURRENCY, (err, results) => {
|
|
|
+ if (err) {
|
|
|
+ alert(err);
|
|
|
+ console.log(err);
|
|
|
+ } else {
|
|
|
+ console.log(results);
|
|
|
+ console.log("photoList处理完毕");
|
|
|
+ this.completeShow = true;
|
|
|
}
|
|
|
- );
|
|
|
+ });
|
|
|
},
|
|
|
//处理单个照片
|
|
|
- async processStudentPhoto(studentPhoto) {
|
|
|
- let photoNameArr = studentPhoto.fileName.split(".");
|
|
|
- let identityNumber = photoNameArr[0]; //文件名就是身份证号码
|
|
|
- let fileSuffix = photoNameArr[1]; //文件后缀
|
|
|
- let photoFile = fs.readFileSync(studentPhoto.path);
|
|
|
+ async processStudentPhoto(studentPhotoPath) {
|
|
|
+ const fileSuffix = path.extname(studentPhotoPath); //文件后缀
|
|
|
+ const identityNumber = path
|
|
|
+ .basename(studentPhotoPath)
|
|
|
+ .replace(fileSuffix, ""); //文件名就是身份证号码
|
|
|
+ const photoFile = fs.readFileSync(studentPhotoPath);
|
|
|
+ const rootOrgId = localStorage.getItem("rootOrgId");
|
|
|
+
|
|
|
//生成新名称
|
|
|
- let md5Hash = CryptoJS.MD5(
|
|
|
- Base64.encode(identityNumber + new Date().getTime())
|
|
|
- ).toString();
|
|
|
- let rootOrgId = localStorage.getItem("rootOrgId");
|
|
|
+ const upyunPhotoPath = (() => {
|
|
|
+ const md5Hash = CryptoJS.MD5(
|
|
|
+ Base64.encode(identityNumber + new Date().getTime())
|
|
|
+ ).toString();
|
|
|
+ return rootOrgId + "/" + identityNumber + "/" + md5Hash + fileSuffix;
|
|
|
+ })();
|
|
|
+
|
|
|
try {
|
|
|
let studentFaceInfo = await this.getStudentInfo(
|
|
|
rootOrgId,
|
|
@@ -166,26 +154,26 @@ export default {
|
|
|
if (this.faceSetToken == undefined) {
|
|
|
this.faceSetToken = (await this.getFaceSetToken()).data;
|
|
|
}
|
|
|
- console.log("this.faceSetToken: " + this.faceSetToken);
|
|
|
let result = await this.addFaceToSet(this.faceSetToken, faceToken);
|
|
|
if (result == 1) {
|
|
|
//当前faceset已满需要更换
|
|
|
this.faceSetToken = undefined;
|
|
|
}
|
|
|
- await this.saveStudentFaceInfo({
|
|
|
- studentFaceInfoId: studentFaceInfo.id,
|
|
|
+
|
|
|
+ const photoInfo = {
|
|
|
studentId: studentFaceInfo.student.id,
|
|
|
- face_set_token: this.face_set_token,
|
|
|
- face_token: faceToken,
|
|
|
- studentPhoto: studentPhoto,
|
|
|
- file: photoFile,
|
|
|
+ faceSetToken: this.faceSetToken,
|
|
|
+ faceToken: faceToken,
|
|
|
+ studentPhotoPath: studentPhotoPath,
|
|
|
rootOrgId: rootOrgId,
|
|
|
- photoPath:
|
|
|
- rootOrgId + "/" + identityNumber + "/" + md5Hash + "." + fileSuffix
|
|
|
- });
|
|
|
+ upyunPhotoPath
|
|
|
+ };
|
|
|
+ await this.saveImageToUpyun({ upyunPhotoPath, photoFile });
|
|
|
+ await this.saveStudentFaceInfoByPut(photoInfo);
|
|
|
+ this.finishOnePhotoSuccess("处理成功", studentPhotoPath);
|
|
|
} catch (err) {
|
|
|
console.log(err);
|
|
|
- this.finishOne(false, err, studentPhoto);
|
|
|
+ this.finishOnePhotoFail(err, studentPhotoPath);
|
|
|
}
|
|
|
},
|
|
|
async getStudentInfo(rootOrgId, identityNumber) {
|
|
@@ -195,8 +183,7 @@ export default {
|
|
|
"/api/ecs_core/studentFaceInfo/identityNumber?orgId=" +
|
|
|
rootOrgId +
|
|
|
"&identityNumber=" +
|
|
|
- identityNumber,
|
|
|
- {}
|
|
|
+ identityNumber
|
|
|
)
|
|
|
.then(res => {
|
|
|
var studentFaceInfo = res.data;
|
|
@@ -207,44 +194,31 @@ export default {
|
|
|
}
|
|
|
})
|
|
|
.catch(err => {
|
|
|
+ console.log(err);
|
|
|
reject("根据身份证号码查询失败");
|
|
|
});
|
|
|
});
|
|
|
},
|
|
|
- //保存照片信息
|
|
|
- async saveStudentFaceInfo(photoInfo) {
|
|
|
- return this.saveImageToUpyun(photoInfo).then(
|
|
|
- res => {
|
|
|
- //调用core后台接口,对数据进行保存
|
|
|
- if (!photoInfo.studentFaceInfoId) {
|
|
|
- return this.saveStudentFaceInfoByPost(photoInfo);
|
|
|
- } else {
|
|
|
- return this.saveStudentFaceInfoByPut(photoInfo);
|
|
|
- }
|
|
|
- },
|
|
|
- err => {
|
|
|
- this.finishOne(false, "saveImageToUpyun失败", photoInfo.studentPhoto);
|
|
|
- }
|
|
|
- );
|
|
|
- },
|
|
|
//保存文件至又拍云
|
|
|
- async saveImageToUpyun(photoInfo) {
|
|
|
- let fileBlob = new Blob([photoInfo.file]);
|
|
|
- var url = process.env.VUE_APP_UPYUN_BUCKETURL + photoInfo.photoPath;
|
|
|
- var authorization =
|
|
|
+ async saveImageToUpyun({ upyunPhotoPath, photoFile }) {
|
|
|
+ const url = process.env.VUE_APP_UPYUN_BUCKETURL + upyunPhotoPath;
|
|
|
+ const authorization =
|
|
|
"Basic " +
|
|
|
Base64.encode(
|
|
|
process.env.VUE_APP_UPYUN_OPERATOR +
|
|
|
":" +
|
|
|
process.env.VUE_APP_UPYUN_PASSWORD
|
|
|
);
|
|
|
- var headers = {
|
|
|
+ const headers = {
|
|
|
headers: {
|
|
|
Authorization: authorization,
|
|
|
"Content-Type": "image/jpeg"
|
|
|
}
|
|
|
};
|
|
|
- return this.$http.put(url, photoInfo.file, headers);
|
|
|
+ return this.$http.put(url, photoFile, headers).catch(err => {
|
|
|
+ console.log(err);
|
|
|
+ throw "saveImageToUpyun失败";
|
|
|
+ });
|
|
|
},
|
|
|
//获取faceSetToken
|
|
|
async getFaceSetToken() {
|
|
@@ -278,6 +252,7 @@ export default {
|
|
|
}
|
|
|
})
|
|
|
.catch(err => {
|
|
|
+ console.log(err);
|
|
|
reject("faceToken加入faceSetToken失败: addface catch error");
|
|
|
});
|
|
|
});
|
|
@@ -299,95 +274,68 @@ export default {
|
|
|
if (res.data.faces.length >= 1) {
|
|
|
resolve(res.data.faces[0].face_token);
|
|
|
} else {
|
|
|
- //this.finishOne(false, 'face++没有检测到人脸', studentPhoto);
|
|
|
reject("face++没有检测到人脸;");
|
|
|
}
|
|
|
})
|
|
|
.catch(err => {
|
|
|
- //this.finishOne(false, '调用face++检测人脸失败', studentPhoto);
|
|
|
+ console.log(err);
|
|
|
reject("调用face++检测人脸失败");
|
|
|
});
|
|
|
});
|
|
|
},
|
|
|
- async saveStudentFaceInfoByPost(photoInfo) {
|
|
|
- return this.$http
|
|
|
- .post("/api/ecs_core/studentFaceInfo", {
|
|
|
- rootOrgId: photoInfo.rootOrgId,
|
|
|
- studentId: photoInfo.studentId,
|
|
|
- faceSetToken: photoInfo.face_set_token,
|
|
|
- faceToken: photoInfo.face_token,
|
|
|
- photoPath: photoInfo.photoPath
|
|
|
- })
|
|
|
- .then(
|
|
|
- res => {
|
|
|
- this.finishOne(true, "处理成功", photoInfo.studentPhoto);
|
|
|
- },
|
|
|
- err => {
|
|
|
- this.finishOne(
|
|
|
- false,
|
|
|
- "saveStudentFaceInfoByPost失败",
|
|
|
- photoInfo.studentPhoto
|
|
|
- );
|
|
|
- }
|
|
|
- );
|
|
|
- },
|
|
|
- async saveStudentFaceInfoByPut(photoInfo) {
|
|
|
+ async saveStudentFaceInfoByPut({
|
|
|
+ rootOrgId,
|
|
|
+ studentId,
|
|
|
+ faceSetToken,
|
|
|
+ faceToken,
|
|
|
+ upyunPhotoPath
|
|
|
+ }) {
|
|
|
return this.$http
|
|
|
.put("/api/ecs_core/studentFaceInfo", {
|
|
|
- rootOrgId: photoInfo.rootOrgId,
|
|
|
- studentId: photoInfo.studentId,
|
|
|
- faceSetToken: photoInfo.face_set_token,
|
|
|
- faceToken: photoInfo.face_token,
|
|
|
- photoPath: photoInfo.photoPath
|
|
|
+ rootOrgId,
|
|
|
+ studentId,
|
|
|
+ faceSetToken,
|
|
|
+ faceToken,
|
|
|
+ photoPath: upyunPhotoPath
|
|
|
})
|
|
|
- .then(
|
|
|
- res => {
|
|
|
- this.finishOne(true, "处理成功", photoInfo.studentPhoto);
|
|
|
- },
|
|
|
- err => {
|
|
|
- this.finishOne(
|
|
|
- false,
|
|
|
- "saveStudentFaceInfoByPut失败",
|
|
|
- photoInfo.studentPhoto
|
|
|
- );
|
|
|
- }
|
|
|
- );
|
|
|
+ .catch(err => {
|
|
|
+ console.log(err);
|
|
|
+ throw "saveStudentFaceInfoByPut失败";
|
|
|
+ });
|
|
|
},
|
|
|
//成功或失败处理
|
|
|
- finishOne(isSuccess, msg, studentPhoto) {
|
|
|
- var fileName = studentPhoto.fileName;
|
|
|
- studentPhoto.finished = true;
|
|
|
+ finishOnePhotoFail(msg, studentPhotoPath) {
|
|
|
+ const fileName = path.basename(studentPhotoPath);
|
|
|
this.returnMsgList.push({
|
|
|
- success: isSuccess,
|
|
|
- fileName: fileName,
|
|
|
- msg: msg
|
|
|
+ success: false,
|
|
|
+ fileName,
|
|
|
+ msg
|
|
|
});
|
|
|
- if (isSuccess) {
|
|
|
- this.successNum++;
|
|
|
- } else {
|
|
|
- this.errorNum++;
|
|
|
- const pathSeperator = require("path").sep;
|
|
|
- var errorfilePath =
|
|
|
- studentPhoto.path.substring(
|
|
|
- 0,
|
|
|
- studentPhoto.path.lastIndexOf(pathSeperator)
|
|
|
- ) +
|
|
|
- pathSeperator +
|
|
|
- "errorfiles";
|
|
|
- if (!fs.existsSync(errorfilePath)) {
|
|
|
- fs.mkdirSync(errorfilePath);
|
|
|
- }
|
|
|
- var readStream = fs.createReadStream(studentPhoto.path);
|
|
|
- var writeStream = fs.createWriteStream(
|
|
|
- errorfilePath + pathSeperator + fileName
|
|
|
- );
|
|
|
- readStream.pipe(writeStream);
|
|
|
- fs.appendFile(
|
|
|
- errorfilePath + pathSeperator + "errorPhotos.txt",
|
|
|
- fileName + ":" + msg + "\n",
|
|
|
- function() {}
|
|
|
- );
|
|
|
+ this.errorNum++;
|
|
|
+
|
|
|
+ //移动照片到errorfiles文件夹
|
|
|
+ const errorfilePath = path.join(
|
|
|
+ path.dirname(studentPhotoPath),
|
|
|
+ "errorfiles"
|
|
|
+ );
|
|
|
+ if (!fs.existsSync(errorfilePath)) {
|
|
|
+ fs.mkdirSync(errorfilePath);
|
|
|
}
|
|
|
+ fs.copyFileSync(studentPhotoPath, path.join(errorfilePath, fileName));
|
|
|
+
|
|
|
+ fs.appendFile(
|
|
|
+ path.join(errorfilePath, "errorPhotos.txt"),
|
|
|
+ fileName + ":" + msg + "\n"
|
|
|
+ );
|
|
|
+ },
|
|
|
+ //成功处理一张照片
|
|
|
+ finishOnePhotoSuccess(msg, studentPhotoPath) {
|
|
|
+ this.returnMsgList.push({
|
|
|
+ success: true,
|
|
|
+ fileName: path.basename(studentPhotoPath),
|
|
|
+ msg: msg
|
|
|
+ });
|
|
|
+ this.successNum++;
|
|
|
},
|
|
|
logout() {
|
|
|
localStorage.removeItem("rootOrgId");
|