index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <style>
  2. </style>
  3. <template>
  4. <div style='height: 100%;'>
  5. <div id='import-header'>
  6. <img src='../assets/images/nav_images.png' alt=''>照片批量上传工具
  7. <button id='exit-btn' @click='logout' title="退出"></button>
  8. </div>
  9. <div id='import-body'>
  10. <div id='import-div'>
  11. <a href='javascript:;' class='file'>
  12. <!-- webkitdirectory-->
  13. <!--<input id='photos' :disabled='uploadDisabled' multiple="true" title=' ' name='files' type='file' v-on:change='importPhotos'/>-->
  14. <button id='import-btn' @click="importPhotos">
  15. 上传照片
  16. </button>
  17. </a>
  18. <div id='progressDiv'>
  19. <span>
  20. 成功:
  21. <span style="color:green;">{{successNum}}</span>/{{allNum}}
  22. </span>
  23. <span>
  24. 失败:<span style="color:red;">{{errorNum}}</span>/{{allNum}}
  25. </span>
  26. <span v-show="completeShow" style="color: green;font-weight: bold;">全部处理完成</span>
  27. <span v-show="!completeShow&&(successNum>0||errorNum>0)" style="color: red;font-weight: bold;">处理中...</span>
  28. </div>
  29. </div>
  30. <div id='progress-div'>
  31. <div id='console-panel'>
  32. <div v-for='msgInfo in returnMsgList'>
  33. <p class='console-line' v-if='msgInfo.success'>
  34. <span><img src='../assets/images/icon_sucess.png'></span>
  35. <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
  36. </p>
  37. <p class='console-line' v-if='!msgInfo.success'>
  38. <span><img src='../assets/images/icon_failed.png'></span>
  39. <span>{{msgInfo.fileName}}:{{msgInfo.msg}}</span>
  40. </p>
  41. </div>
  42. </div>
  43. </div>
  44. </div>
  45. </div>
  46. </template>
  47. <script>
  48. const CryptoJS = require('crypto-js');
  49. const Base64 = require('js-base64').Base64;
  50. const fs = nodeRequire("fs");
  51. import global_ from '../store/global';
  52. export default {
  53. data() {
  54. return {
  55. photoQuantityLimit:10000,//单次上传限制
  56. photoList:[],//总的照片队列
  57. photoLine:[],//正在处理的照片队列
  58. concurrency:5,//同时处理的照片数量
  59. returnMsgList:[],//返回信息
  60. successNum:0, //成功数量
  61. errorNum:0, //失败数量
  62. allNum:0, //总数
  63. completeShow:false,
  64. uploadDisabled:false,
  65. seconds:0 // 耗时 秒
  66. }
  67. },
  68. methods: {
  69. init(){
  70. this.photoList = [];
  71. this.photoLine = [];
  72. this.returnMsgList = [];
  73. this.successNum = 0;
  74. this.errorNum = 0;
  75. this.allNum = 0;
  76. this.completeShow = false;
  77. },
  78. importPhotos() {
  79. this.init();
  80. var electron = nodeRequire('electron');
  81. let dialog = electron.remote.dialog;
  82. dialog.showOpenDialog({
  83. title: '选择照片所在目录',
  84. properties: ["openDirectory"]
  85. }, (folderPaths) => {
  86. if (folderPaths) {
  87. this.processQueue(folderPaths[0]);
  88. }
  89. });
  90. },
  91. processQueue(folderPath){
  92. this.readFiles(folderPath);
  93. this.allNum = this.photoList.length;
  94. var _this = this;
  95. //初始化this.concurrency个到photoLine中,开始处理
  96. this.photoLine = this.photoList.splice(0,this.concurrency);
  97. for(var i = 0;i<this.photoLine.length;i++){
  98. this.processStudentPhoto(this.photoLine[i]);
  99. }
  100. /**
  101. * 启动检查,看photoLine中是否有已完成的,如果有,从photoList中取出第一个将其替换
  102. * 队列中一直保持this.concurrency个正在处理的照片
  103. */
  104. global_.pageTimer['uploadTimer'] = setInterval(function(){
  105. for (let i = 0; i < _this.photoLine.length; i++) {
  106. if (_this.photoLine[i].finished) {
  107. if (_this.photoList.length > 0) {
  108. _this.photoLine[i] = _this.photoList.shift();
  109. _this.processStudentPhoto(_this.photoLine[i]);
  110. }
  111. }
  112. }
  113. if (_this.photoList.length < 1) {
  114. console.log('photoList处理完毕');
  115. _this.completeShow = true;
  116. clearInterval(global_.pageTimer['uploadTimer']);
  117. }
  118. },1500);
  119. },
  120. //读取文件
  121. readFiles(folderPath) {
  122. let files = fs.readdirSync(folderPath);
  123. files.forEach((fileName)=> {
  124. let fullPath = folderPath + "\\" + fileName;
  125. /*var stat = fs.lstatSync(fullPath);
  126. if (stat.isDirectory()) {
  127. this.readFiles(fullPath);
  128. } else if (this.isImageFile(file)) {
  129. var student = {}
  130. student.fileName = file;
  131. student.path = fullPath;
  132. this.photoList.push(student);
  133. }*/
  134. //只扫描当前文件夹,不递归扫描
  135. if (this.isImageFile(fileName)) {
  136. var student = {}
  137. student.fileName = fileName;
  138. student.path = fullPath;
  139. this.photoList.push(student);
  140. }
  141. });
  142. },
  143. isImageFile(fileName) {
  144. let lowercaseName = fileName.toLowerCase();
  145. if (lowercaseName.endsWith(".jpg") || lowercaseName.endsWith(".jpeg") || lowercaseName.endsWith(".png")) {
  146. return true;
  147. } else {
  148. return false;
  149. }
  150. },
  151. processStudentPhoto(studentPhoto) {
  152. let photoNameArr = studentPhoto.fileName.split('.');
  153. let fileName = photoNameArr[0];//文件名就是身份证号码
  154. let fileSuffix = photoNameArr[1];//文件后缀
  155. let photo = fs.readFileSync(studentPhoto.path);
  156. //生成新名称
  157. let md5Hash = CryptoJS.MD5(Base64.encode(fileName+new Date().getTime())).toString();
  158. let rootOrgId = localStorage.getItem('rootOrgId');
  159. //根据身份证号码查询学生信息
  160. this.$http.get('/api/ecs_core/studentFaceInfo/identityNumber?orgId='+rootOrgId+'&identityNumber='+fileName, {})
  161. .then((response) => {
  162. var studentFaceInfo = response.body;
  163. if (studentFaceInfo.student && studentFaceInfo.student.id) {
  164. this.detectFace(photo).then((res)=>{
  165. if(res.body.faces.length>=1){
  166. let face_token = res.body.faces[0].face_token;
  167. this.getFaceSetToken().then((res)=>{
  168. //var face_set_token = res.bodyText;
  169. var face_set_token = '0c430026552ffcf4a8172e7182c91ae8';
  170. this.addFaceToSet(face_set_token, face_token).then((res)=>{
  171. this.saveStudentFaceInfo({
  172. 'studentFaceInfoId':studentFaceInfo.id,
  173. 'studentId':studentFaceInfo.student.id,
  174. 'face_set_token':face_set_token,
  175. 'face_token':face_token,
  176. 'studentPhoto':studentPhoto,
  177. 'identityNumber':fileName,
  178. 'fileSuffix':fileSuffix,
  179. 'photoNewName':md5Hash+'.'+fileSuffix,
  180. 'file':photo
  181. });
  182. },(err)=>{
  183. this.finishOne(false,'addFaceToSet失败',studentPhoto);
  184. });
  185. },(err)=>{
  186. this.finishOne(false,'获取FaceSetToken失败',studentPhoto);
  187. });
  188. }else{
  189. this.finishOne(false,'face++没有检测到人脸',studentPhoto);
  190. }
  191. },(err)=>{
  192. this.finishOne(false,'调用face++检测人脸失败',studentPhoto);
  193. });
  194. }else{
  195. this.finishOne(false,'查询身份证不存在',studentPhoto);
  196. }
  197. },(err)=>{
  198. this.finishOne(false,'根据身份证号码查询失败',studentPhoto);
  199. });
  200. },
  201. //保存照片信息
  202. saveStudentFaceInfo(photoInfo){
  203. this.saveImageToUpyun(photoInfo).then((res)=>{
  204. //调用core后台接口,对数据进行保存
  205. if(!photoInfo.studentFaceInfoId){
  206. this.saveStudentFaceInfoByPost(photoInfo);
  207. }else{
  208. this.saveStudentFaceInfoByPut(photoInfo);
  209. }
  210. },(err)=>{
  211. this.finishOne(false,'saveImageToUpyun失败',photoInfo.studentPhoto);
  212. });
  213. },
  214. //保存文件至又拍云
  215. saveImageToUpyun(photoInfo){
  216. let fileBlob = new Blob([photoInfo.file]);
  217. var url = process.env.VUE_APP_UPYUN_BUCKETURL+'/student_base_photo/'+photoInfo.identityNumber+'/'+photoInfo.photoNewName;
  218. var authorization = 'Basic '+Base64.encode(process.env.VUE_APP_UPYUN_OPERATOR+':'+process.env.VUE_APP_UPYUN_PASSWORD);
  219. var headers = {headers:{'Authorization':authorization,'Content-Type': 'image/jpeg'}};
  220. return this.$http.put(url,photoInfo.file,headers);
  221. },
  222. //获取faceSetToken
  223. getFaceSetToken(){
  224. return this.$http.get('/api/ecs_core/faceSet/enableFaceSet');
  225. },
  226. //faceToken加入faceSetToken
  227. addFaceToSet(faceset_token,face_token){
  228. let formData_addface = new FormData();
  229. formData_addface.append('api_key', process.env.VUE_APP_FACEPP_API_KEY);
  230. formData_addface.append('api_secret',process.env.VUE_APP_FACEPP_API_SECRET);
  231. formData_addface.append('faceset_token', faceset_token);
  232. formData_addface.append('face_tokens', face_token);
  233. return this.$http.post('/facepp/v3/faceset/addface', formData_addface);
  234. },
  235. //face++分析人脸
  236. detectFace(file){
  237. let fileBlob = new Blob([file]);
  238. let formData_face_token = new FormData();
  239. formData_face_token.append('api_key',process.env.VUE_APP_FACEPP_API_KEY);
  240. formData_face_token.append('api_secret',process.env.VUE_APP_FACEPP_API_SECRET);
  241. formData_face_token.append('image_file', fileBlob);
  242. return this.$http.post('/facepp/v3/detect', formData_face_token);
  243. },
  244. saveStudentFaceInfoByPost(photoInfo){
  245. this.$http.post('/api/ecs_core/studentFaceInfo', {
  246. 'faceSetToken': photoInfo.face_set_token,
  247. 'faceToken': photoInfo.face_token,
  248. 'photoMD5': photoInfo.photoNewName,
  249. 'student': {
  250. 'id': photoInfo.studentId
  251. },
  252. 'createUser':localStorage.getItem('userName'),
  253. 'updateUser':localStorage.getItem('userName'),
  254. 'createType':'IMPORT_TOOL'
  255. }).then((res)=>{
  256. this.finishOne(true,'处理成功',photoInfo.studentPhoto);
  257. },(err)=>{
  258. this.finishOne(false,'saveStudentFaceInfoByPost失败',photoInfo.studentPhoto);
  259. });
  260. },
  261. saveStudentFaceInfoByPut(photoInfo){
  262. this.$http.put('/api/ecs_core/studentFaceInfo', {
  263. 'faceSetToken': photoInfo.face_set_token,
  264. 'faceToken': photoInfo.face_token,
  265. 'photoMD5': photoInfo.photoNewName,
  266. 'student': {
  267. 'id': photoInfo.studentId
  268. },
  269. 'createUser':localStorage.getItem('userName'),
  270. 'updateUser':localStorage.getItem('userName'),
  271. 'createType':'IMPORT_TOOL'
  272. }).then((res)=>{
  273. this.finishOne(true,'处理成功',photoInfo.studentPhoto);
  274. },(err)=>{
  275. this.finishOne(false,'saveStudentFaceInfoByPut失败',photoInfo.studentPhoto);
  276. });
  277. },
  278. //成功或失败处理
  279. finishOne(isSuccess,msg,studentPhoto){
  280. var fileName = studentPhoto.fileName;
  281. studentPhoto.finished = true;
  282. this.returnMsgList.push({'success':isSuccess,'fileName':fileName,'msg':msg});
  283. if(isSuccess){
  284. this.successNum++;
  285. }else{
  286. this.errorNum++;
  287. var errorfilePath = studentPhoto.path.substring(0,studentPhoto.path.lastIndexOf("\\"))+"\\errorfiles";
  288. if (!fs.existsSync(errorfilePath)) {
  289. fs.mkdirSync(errorfilePath);
  290. }
  291. var readStream = fs.createReadStream(studentPhoto.path);
  292. var writeStream = fs.createWriteStream(errorfilePath+"\\"+fileName);
  293. readStream.pipe(writeStream);
  294. fs.appendFile(errorfilePath+'\\errorPhotos.txt',fileName+":"+msg+'\n',function (){});
  295. }
  296. },
  297. logout() {
  298. localStorage.removeItem("rootOrgId");
  299. localStorage.removeItem("userName");
  300. localStorage.removeItem("user_token");
  301. this.$router.push({path:'/login'});
  302. }
  303. }
  304. };
  305. </script>