Răsfoiți Sursa

重构上传照片异步流程。无数据库直连功能。

Michael Wang 6 ani în urmă
părinte
comite
26c31e8462
2 a modificat fișierele cu 171 adăugiri și 203 ștergeri
  1. 22 2
      src/main.js
  2. 149 201
      src/views/index.vue

+ 22 - 2
src/main.js

@@ -7,8 +7,6 @@ import "./assets/bootstrap/css/bootstrap.min.css";
 import "./assets/css/common.css";
 import "./assets/css/index.css";
 
-// import global_ from "./store/global";
-
 import axios from "axios";
 const axiosRetry = require("axios-retry");
 
@@ -18,6 +16,7 @@ Vue.use(ElementUI);
 
 const qmInstance = axios.create({});
 let wk_token;
+let requestInProcessTotal = 0;
 qmInstance.interceptors.request.use(
   config => {
     if (config.url.includes("/login") === false) {
@@ -31,6 +30,8 @@ qmInstance.interceptors.request.use(
       wk_token = null;
     }
 
+    console.log("后台并发峰值:" + requestInProcessTotal++);
+
     return config;
   },
   error => {
@@ -42,6 +43,7 @@ qmInstance.interceptors.request.use(
 //响应拦截
 qmInstance.interceptors.response.use(
   response => {
+    requestInProcessTotal--;
     return response;
   },
   error => {
@@ -51,6 +53,24 @@ qmInstance.interceptors.response.use(
       console.log(error);
     }
 
+    // 判断reponse的内容,如果是no login就退出
+    if (
+      error.response.data.code === "403" &&
+      error.response.data.desc === "no login."
+    ) {
+      wk_token = null;
+      localStorage.removeItem("rootOrgId");
+      localStorage.removeItem("userName");
+      localStorage.removeItem("user_token");
+      if (Date.now() - (window.logAlertDate || 0) > 10000) {
+        window.logAlertDate = Date.now();
+        alert("登录失效 跳转登录页面");
+      }
+      router.push({
+        name: "login"
+      });
+    }
+
     // 登录失效 跳转登录页面 //facepp可能返回403
     // if (status == 403 || status == 401) {
     //   wk_token = null;

+ 149 - 201
src/views/index.vue

@@ -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");