tools_batch_files 1.0.21 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -30,10 +30,10 @@ const SOURCE_VIDEO_DIR = "source_video";
30
30
  const ZIP_FILES_DIR = "zip";
31
31
 
32
32
  //并发数量
33
- const queueCount = 10;
33
+ const queueCount = 50;
34
34
 
35
35
  //起始任务下标
36
- let taskIndex = 0;
36
+ let taskIndex = 2066;
37
37
 
38
38
  /**
39
39
  * 递归遍历文件夹,查找mp4文件
@@ -102,12 +102,12 @@ const logger = (log, hash, err) => {
102
102
  };
103
103
 
104
104
  //错误日志
105
- const disposeError = (hash, err) => {
106
- logger("---任务失败---", hash, err);
105
+ const disposeError = (fileName) => {
106
+ logger("---任务失败---" + fileName);
107
107
  logger(
108
108
  `******************************************************************end`
109
109
  );
110
- fs.writeFileSync("error.txt", err + "\n", {
110
+ fs.writeFileSync("error.txt", fileName + "\n", {
111
111
  flag: "a",
112
112
  });
113
113
  };
@@ -422,7 +422,7 @@ const getMetadata = async (
422
422
  video_id: rowFileName,
423
423
  title,
424
424
  keyword,
425
- pr: 90,
425
+ pr: 100,
426
426
  format: "mp4",
427
427
  category_id: 4,
428
428
  demand_kind: 23,
@@ -485,10 +485,11 @@ const postData = (dataParams, indexFilePath) => {
485
485
 
486
486
  logger("等待接口返回结果……");
487
487
 
488
- return axios.post("http://192.168.101.149:9999/upload/video", formData, {
488
+ return axios.post("http://127.0.0.1:9999/upload/video", formData, {
489
489
  headers: {
490
490
  "Content-Type": "multipart/form-data",
491
491
  },
492
+ timeout: 600000,
492
493
  });
493
494
  };
494
495
 
@@ -509,7 +510,7 @@ const task = async (row, index) => {
509
510
  const keyword = filteredArray.join(" ");
510
511
 
511
512
  if (!fileName.includes(".")) {
512
- fileName = row.fileName + ".mp4";
513
+ fileName = row.fileName + ".mov";
513
514
  }
514
515
 
515
516
  // 源视频文件路径
@@ -519,7 +520,7 @@ const task = async (row, index) => {
519
520
  );
520
521
 
521
522
  if (!originFilePath) {
522
- logger(`图片文件 ${fileName} 不存在`);
523
+ logger(`视频文件 ${fileName} 不存在`);
523
524
  return;
524
525
  }
525
526
  logger("源视频文件路径");
@@ -547,10 +548,17 @@ const task = async (row, index) => {
547
548
  keyword,
548
549
  }
549
550
  );
550
- console.log("dataParams参数");
551
- console.log(dataParams);
552
551
 
553
552
  const resData = await postData(dataParams, indexFilePath);
553
+ if (resData.data.code === 200) {
554
+ logger("请求成功!");
555
+ logger(resData.data.code);
556
+ logger(resData.data);
557
+ //删除文件夹
558
+ // fs.rmdirSync(indexFilePath, { recursive: true });
559
+ } else {
560
+ throw new Error("请求失败!" + resData.data.msg);
561
+ }
554
562
  logger(resData.data);
555
563
  logger(
556
564
  `----------------------------------------第${index}条结束---------------------------------end`
@@ -563,7 +571,9 @@ const task = async (row, index) => {
563
571
  logger("视频处理失败:");
564
572
  logger(error);
565
573
  }
566
- disposeError(hash, fileName);
574
+ logger("catch日志*********************************");
575
+ disposeError(fileName);
576
+ // fs.rmdirSync(indexFilePath, { recursive: true });
567
577
  }
568
578
  };
569
579
 
@@ -601,7 +611,7 @@ const transitionExcelToJSON = () => {
601
611
  };
602
612
 
603
613
  for (let i = 0; i < queueCount; i++) {
604
- run(i);
614
+ run(i + taskIndex);
605
615
  }
606
616
  };
607
617
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tools_batch_files",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "批处理视频工具",
5
5
  "keywords": [
6
6
  "utils",
package/src/photoBatch.js CHANGED
@@ -1,536 +1,524 @@
1
- #!/usr/bin/env node
2
- const XLSX = require("xlsx");
3
- const ffmpeg = require("fluent-ffmpeg");
4
- const fs = require("fs");
5
- const path = require("path");
6
- const fs_asnyc = require("fs").promises;
7
- const archiver = require("archiver");
8
- const axios = require("axios");
9
- const generateUniqueHash = require("../utils/index");
10
- const FormData = require("form-data");
11
-
12
- //执行目录
13
- const exeDir = __dirname;
14
- //当前工作目录
15
- const workDir = process.cwd();
16
-
17
- // 表格目录
18
- const excelDir = path.join(workDir, "excel", "excel.xlsx");
19
- //水印
20
- const watermarkImage = path.join(__dirname, "..", "imgs", "picWater.png");
21
-
22
- // 截图数量
23
- const SCREENSHOT_COUNT = 5;
24
- const ORIGIN_FILE_DIR = "photos";
25
- const PHOTO_PREVIEW_DIR = "preview_photo";
26
- const ZIP_VIDEO_DIR = "zip_video";
27
- const ZIP_VIDEO_DIR_400 = "small_video";
28
- const SCREENSHOT_DIR = "preview_photo";
29
- const SCREENSHOT_WATERMARK_DIR = "screenshots_watermark";
30
- const ZIP_WATERMARK_VIDEO_DIR = "zip_watermark_video";
31
- const ZIP_SCREENSHOT_WM_DIR = "preview_watermark_photo";
32
- const SOURCE_PHOTO_DIR = "source_photo";
33
- const ZIP_FILES_DIR = "zip";
34
-
35
- const expandedName = ".jpg";
36
-
37
- //并发数量
38
- const queueCount = 50;
39
-
40
- //起始任务下标
41
- let taskIndex = 88943;
42
-
43
- const readExcel = (path) => {
44
- const workbook = XLSX.readFile(path);
45
-
46
- // 获取第一个工作表(Sheet)
47
- const firstSheetName = workbook.SheetNames[0];
48
- const worksheet = workbook.Sheets[firstSheetName];
49
-
50
- // 将工作表转换为 JSON 对象
51
- const jsonData = XLSX.utils.sheet_to_json(worksheet);
52
- return jsonData;
53
- };
54
-
55
- function logFileSize(path) {
56
- const fileSize = fs.statSync(path).size / (1024 * 1024);
57
- const formattedSize = fileSize.toFixed(2); // 保留两位小数
58
- logger(`图片大小:${formattedSize}M`);
59
- }
60
-
61
- //普通日志
62
- const logger = (log, hash, err) => {
63
- console.log(log);
64
- fs.writeFileSync("log.txt", log + "\n", {
65
- flag: "a",
66
- });
67
- };
68
-
69
- //错误日志
70
- const disposeError = (err) => {
71
- logger("---任务失败---", err);
72
- logger(
73
- `******************************************************************end`
74
- );
75
- fs.writeFileSync("error.txt", err + "\n", {
76
- flag: "a",
77
- });
78
- };
79
-
80
- /**
81
- * 递归遍历文件夹,查找文件
82
- * @param {*} dir 文件夹路径
83
- * @param {*} fileName 文件名称
84
- * @returns string 匹配成功的文件路径
85
- */
86
- function findFileInDir(dir, fileName) {
87
- const files = fs.readdirSync(dir);
88
- for (const file of files) {
89
- const filePath = path.join(dir, file);
90
- const stat = fs.statSync(filePath);
91
- if (stat.isDirectory()) {
92
- const result = findFileInDir(filePath, fileName);
93
- if (result) {
94
- return result;
95
- }
96
- } else if (file.startsWith(fileName) && file.endsWith(expandedName)) {
97
- return filePath;
98
- }
99
- }
100
- return null;
101
- }
102
-
103
- // function findFileInDir(dir, fileName) {
104
- // return new Promise((resolve, reject) => {
105
- // fs.readdir(dir, (err, files) => {
106
- // if (err) {
107
- // reject(err);
108
- // return;
109
- // }
110
-
111
- // // 遍历目录中的文件和子目录
112
- // for (const file of files) {
113
- // const filePath = path.join(dir, file);
114
- // fs.stat(filePath, (err, stat) => {
115
- // if (err) {
116
- // reject(err); // 如果获取文件状态出错,直接reject
117
- // return;
118
- // }
119
- // if (stat.isDirectory()) {
120
- // // 如果是目录,递归调用findFileInDir
121
- // findFileInDir(filePath, fileName)
122
- // .then(resolve) // 如果找到了目标文件,将结果resolve
123
- // .catch(reject); // 如果在子目录中出错,将错误reject
124
- // } else if (file.startsWith(fileName) && file.endsWith(expandedName)) {
125
- // resolve(filePath); // 如果找到了目标文件,将结果resolve
126
- // }
127
- // });
128
- // }
129
-
130
- // // 如果遍历完了所有文件和子目录仍未找到目标文件,resolve一个null表示未找到
131
- // resolve(null);
132
- // });
133
- // });
134
- // }
135
-
136
- /**
137
- * 生成预览图 indexFilePath: output/0
138
- * originFilePath E:\workspace\图片测试\photos\abc222.png
139
- */
140
- const PriviewPhoto = (indexFilePath, originFilePath, fileName) => {
141
- //存放预览图图的文件夹
142
- const outputDir = path.join(indexFilePath, SCREENSHOT_DIR);
143
- ensureDirSync(outputDir);
144
-
145
- const outputFile = path.join(outputDir, fileName);
146
-
147
- return new Promise((resolve, reject) => {
148
- const watermarkScreenshotsComand = ffmpeg(originFilePath);
149
- watermarkScreenshotsComand
150
- .size("1280x?")
151
- .output(outputFile)
152
- .on("error", (err) => {
153
- logger("截图预览图出错: " + err);
154
- reject();
155
- })
156
- .on("end", () => {
157
- logger("预览图完成: " + outputFile);
158
- resolve();
159
- })
160
- .run();
161
- });
162
- };
163
-
164
- /**
165
- * 生成水印预览图
166
- */
167
- const watermarkPriviewPhoto = (indexFilePath, originFilePath, fileName) => {
168
- //存放水印截图的文件夹
169
- const outputDir = path.join(indexFilePath, ZIP_SCREENSHOT_WM_DIR);
170
- const inputDir = path.join(indexFilePath, SCREENSHOT_DIR, fileName);
171
- ensureDirSync(outputDir);
172
-
173
- const outputFile = path.join(outputDir, fileName);
174
-
175
- return new Promise((resolve, reject) => {
176
- const watermarkScreenshotsComand = ffmpeg(inputDir);
177
- watermarkScreenshotsComand
178
- .input(watermarkImage)
179
- .complexFilter("overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2")
180
- .output(outputFile)
181
- .on("error", (err) => {
182
- logger("截图添加水印出错: " + err);
183
- reject();
184
- })
185
- .on("end", () => {
186
- logger("截图水印添加完成: " + outputFile);
187
- resolve();
188
- })
189
- .run();
190
- });
191
- };
192
-
193
- /**
194
- * 确保目录存在,如果不存在则创建它
195
- */
196
- function ensureDirSync(dirpath) {
197
- try {
198
- if (!fs.existsSync(dirpath)) {
199
- fs.mkdirSync(dirpath, { recursive: true });
200
- logger(`目录创建成功:${dirpath}`);
201
- }
202
- } catch (err) {
203
- logger(`创建目录时出错:${err}`);
204
- throw err;
205
- }
206
- }
207
-
208
- /**
209
- * 打包物料
210
- */
211
- const archiveZip = (fileName, inputPath, originFilePath) => {
212
- const zipDir = path.join(inputPath, "zip");
213
- const timestamp = new Date().getTime();
214
-
215
- ensureDirSync(zipDir);
216
- const zipStream = fs.createWriteStream(
217
- path.join(zipDir, `package${timestamp}.zip`)
218
- );
219
- const archive = archiver("zip", {
220
- zlib: { level: 9 },
221
- });
222
-
223
- return new Promise((resolve, reject) => {
224
- zipStream.on("close", function () {
225
- logger("压缩数据:" + archive.pointer() + " total bytes");
226
- logger(
227
- "完成归档archiver has been finalized and the output file descriptor has closed."
228
- );
229
- resolve();
230
- });
231
-
232
- archive.on("warning", function (err) {
233
- if (err.code === "ENOENT") {
234
- logger("压缩-warning:" + err);
235
- } else {
236
- throw err;
237
- }
238
- });
239
-
240
- archive.on("error", function (err) {
241
- logger("压缩失败!" + err);
242
- reject();
243
- });
244
-
245
- archive.pipe(zipStream);
246
- const directories = [ZIP_SCREENSHOT_WM_DIR, SCREENSHOT_DIR];
247
-
248
- directories.forEach((dir) => {
249
- const dirPath = path.join(inputPath, dir);
250
- archive.directory(dirPath, dir);
251
- });
252
-
253
- archive.file(originFilePath, {
254
- name: path.join(SOURCE_PHOTO_DIR, fileName),
255
- });
256
- // 完成归档
257
- archive.finalize();
258
- });
259
- };
260
-
261
- /**
262
- * 获取 元数据
263
- */
264
- const getMetadata = async (
265
- fileName,
266
- indexFilePath,
267
- originFilePath,
268
- { rowFileName, title, keyword }
269
- ) => {
270
- //源图片数据
271
- const photoMetadataComand = ffmpeg(originFilePath);
272
- //预览图数据
273
- const photoInputPath = path.join(indexFilePath, SCREENSHOT_DIR, fileName);
274
- const photoPreviewMetadataComand = ffmpeg(photoInputPath);
275
-
276
- const metaDataParams = {
277
- photo_id: rowFileName,
278
- width: "",
279
- height: "",
280
- format: "",
281
- size: "",
282
- pr: 160,
283
- dpi: 0,
284
- is_government: 0,
285
- common_width: "",
286
- common_height: "",
287
- dujia: 0,
288
- xiaoxiang: 0,
289
- title,
290
- keyword,
291
- en_keyword: "",
292
- // preview_path: "",
293
- // markImage_path: "",
294
- // photo_path: "",
295
- // common_image_path: "",
296
- // en_keyword: "",
297
- plate_id: 3,
298
- transform_plate_id: 3,
299
- tag_id: 47,
300
- userid: 1003,
301
- username: "美好景象",
302
- demand_kind: 3,
303
- source_from: 71,
304
- file: "",
305
- };
306
-
307
- await new Promise((resolve, reject) => {
308
- photoMetadataComand.ffprobe(function (err, metadata) {
309
- // console.log("metadata");
310
- // console.log(JSON.stringify(metadata));
311
- if (metadata) {
312
- const formatStream = metadata.format;
313
- const photoStream = metadata.streams.find(
314
- (s) => s.codec_type === "video"
315
- );
316
-
317
- metaDataParams.width = photoStream.width;
318
- metaDataParams.height = photoStream.height;
319
- // metaDataParams.format = formatStream.format_name;
320
- metaDataParams.format = "jpg";
321
- metaDataParams.size = formatStream.size;
322
-
323
- resolve();
324
- } else {
325
- reject(err);
326
- }
327
- });
328
- });
329
- await new Promise((resolve, reject) => {
330
- photoPreviewMetadataComand.ffprobe(function (err, metadata) {
331
- if (metadata) {
332
- const photoStream = metadata.streams.find(
333
- (s) => s.codec_type === "video"
334
- );
335
-
336
- metaDataParams.common_width = photoStream.width;
337
- metaDataParams.common_height = photoStream.height;
338
- resolve();
339
- } else {
340
- reject(err);
341
- }
342
- });
343
- });
344
-
345
- return metaDataParams;
346
- };
347
-
348
- /**
349
- * 接口
350
- */
351
- const postData = (dataParams, indexFilePath) => {
352
- const formData = new FormData();
353
-
354
- const zipFiles = fs
355
- .readdirSync(path.join(indexFilePath, ZIP_FILES_DIR))
356
- .find((file) => file.endsWith(".zip"));
357
-
358
- const packageZip = path.join(indexFilePath, ZIP_FILES_DIR, zipFiles);
359
-
360
- formData.append("file", fs.createReadStream(packageZip));
361
- for (const key in dataParams) {
362
- if (Object.hasOwnProperty.call(dataParams, key)) {
363
- const value = dataParams[key];
364
- formData.append(key, value);
365
- }
366
- }
367
-
368
- logger("等待接口返回结果……");
369
-
370
- // return axios.post("http://192.168.102.61:9999/upload/photo", formData, {
371
- return axios.post("http://127.0.0.1:9999/upload/photo", formData, {
372
- headers: {
373
- "Content-Type": "multipart/form-data",
374
- },
375
- timeout: 300000,
376
- });
377
- };
378
-
379
- /**
380
- * 任务
381
- */
382
- const task = async (row, index) => {
383
- logger(
384
- "**************************" + row.fileName + "**************************"
385
- );
386
- // Excel的列名分别为: fileName title keyword
387
- // 表格中文件名无后缀,遂手动添加写死为mp4
388
- let fileName = row.fileName;
389
- const rowFileName = row.fileName;
390
- const title = row.title;
391
- const keywordArr = row.keyword.split(",");
392
- const filteredArray = keywordArr.filter((item) => item.trim() !== "");
393
- const keyword = filteredArray.join(" ");
394
-
395
- if (!fileName.includes(".")) {
396
- fileName = row.fileName + expandedName;
397
- }
398
-
399
- const getPathStartTime = new Date();
400
- // 源图片文件夹路径
401
- const originFilePath = await findFileInDir(
402
- path.join(workDir, ORIGIN_FILE_DIR),
403
- fileName
404
- );
405
- const getPathEndTime = new Date();
406
- const timeInSeconds = ((getPathEndTime - getPathStartTime) / 1000).toFixed(2);
407
-
408
- logger(`第${index}条Path路径搜索时间:${timeInSeconds}秒`);
409
- logger(`-------------------${new Date()}------------------`);
410
-
411
- console.log("originFilePath:" + originFilePath);
412
- console.log("fileName:" + fileName);
413
-
414
- if (!originFilePath) {
415
- logger(`图片文件 ${fileName} 不存在`);
416
- return;
417
- }
418
-
419
- //index文件夹 output/0
420
- const indexFilePath = path.join(workDir, "output", index + "");
421
-
422
- try {
423
- const workStartTime = new Date();
424
- await fs_asnyc.access(originFilePath, fs_asnyc.constants.F_OK);
425
- logFileSize(originFilePath);
426
- await PriviewPhoto(indexFilePath, originFilePath, fileName);
427
- await watermarkPriviewPhoto(indexFilePath, originFilePath, fileName);
428
- await archiveZip(fileName, indexFilePath, originFilePath);
429
- const dataParams = await getMetadata(
430
- fileName,
431
- indexFilePath,
432
- originFilePath,
433
- {
434
- rowFileName,
435
- title,
436
- keyword,
437
- }
438
- );
439
- const workEndTime = new Date();
440
- const timeInSeconds = ((workEndTime - workStartTime) / 1000).toFixed(2);
441
-
442
- logger(`第${index}条4090处理总时间:${timeInSeconds}秒`);
443
- logger(`-------------------${new Date()}------------------`);
444
-
445
- const postStartTime = new Date();
446
- const resData = await postData(dataParams, indexFilePath);
447
- const postEndTime = new Date();
448
- const timeInSeconds1 = ((postEndTime - postStartTime) / 1000).toFixed(2);
449
-
450
- logger(`第${index}条接口处理总时间:${timeInSeconds1}秒`);
451
- logger(`-------------------${new Date()}------------------`);
452
-
453
- if (resData.data.code === 200) {
454
- logger("请求成功!");
455
- logger(resData.data.code);
456
- console.log(resData.data);
457
- //删除文件夹
458
- fs.rmdirSync(indexFilePath, { recursive: true });
459
- } else {
460
- throw new Error("请求失败!" + resData.data.msg);
461
- }
462
-
463
- logger(
464
- `----------------------------------------第${index}条结束---------------------------------end`
465
- );
466
- } catch (error) {
467
- // 可以约定code,来表示不同的错误信息
468
- if (error.code === "ENOENT") {
469
- logger(`图片文件 ${fileName} 不存在`);
470
- } else {
471
- logger("图片任务处理失败:" + error);
472
- logger(error);
473
- }
474
- fs.rmdirSync(indexFilePath, { recursive: true });
475
- disposeError(fileName);
476
- }
477
- };
478
-
479
- /**
480
- * 解析Excel数据
481
- */
482
- const transitionExcelToJSON = () => {
483
- //当前任务hash
484
- const hashOrigin = generateUniqueHash();
485
- const hash = hashOrigin.slice(0, 8);
486
- logger("图片批量任务任务开始---" + hash);
487
- logger("当前目录: " + workDir);
488
- logger("工作目录: " + __dirname);
489
-
490
- const jsonData = readExcel(excelDir);
491
-
492
- const run = (index) => {
493
- try {
494
- logger(
495
- `run-------------------------------------第${index}条开始------------------------------------`
496
- );
497
- const readExcelStartTime = new Date();
498
- const row = jsonData[index];
499
-
500
- const readExcelEndTime = new Date();
501
- const timeInSeconds = (
502
- (readExcelEndTime - readExcelStartTime) /
503
- 1000
504
- ).toFixed(2);
505
-
506
- logger(`第${index}条Excel搜索时间:${timeInSeconds}秒`);
507
- logger(`-------------------${new Date()}------------------`);
508
- if (!row) {
509
- return;
510
- }
511
- logger(
512
- `----------------------------------------第${index}条task开始--------------------------------`
513
- );
514
- logger(`-------------------${new Date()}------------------`);
515
- task(row, index)
516
- .then(() => {
517
- taskIndex++;
518
- run(taskIndex);
519
- })
520
- .catch(() => {
521
- taskIndex++;
522
- run(taskIndex);
523
- });
524
- } catch (error) {
525
- logger("捕获错误!" + error);
526
- taskIndex++;
527
- run(taskIndex);
528
- }
529
- };
530
-
531
- for (let i = 0; i < queueCount; i++) {
532
- run(i + taskIndex);
533
- }
534
- };
535
-
536
- transitionExcelToJSON();
1
+ #!/usr/bin/env node
2
+ const XLSX = require("xlsx");
3
+ const ffmpeg = require("fluent-ffmpeg");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const fs_asnyc = require("fs").promises;
7
+ const archiver = require("archiver");
8
+ const axios = require("axios");
9
+ const generateUniqueHash = require("../utils/index");
10
+ const FormData = require("form-data");
11
+
12
+ //执行目录
13
+ const exeDir = __dirname;
14
+ //当前工作目录
15
+ const workDir = process.cwd();
16
+
17
+ // 表格目录
18
+ const excelDir = path.join(workDir, "excel", "excel.xlsx");
19
+ //水印
20
+ const watermarkImage = path.join(__dirname, "..", "imgs", "picWater.png");
21
+
22
+ // 截图数量
23
+ const SCREENSHOT_COUNT = 5;
24
+ const ORIGIN_FILE_DIR = "photos";
25
+ const PHOTO_PREVIEW_DIR = "preview_photo";
26
+ const ZIP_VIDEO_DIR = "zip_video";
27
+ const ZIP_VIDEO_DIR_400 = "small_video";
28
+ const SCREENSHOT_DIR = "preview_photo";
29
+ const SCREENSHOT_WATERMARK_DIR = "screenshots_watermark";
30
+ const ZIP_WATERMARK_VIDEO_DIR = "zip_watermark_video";
31
+ const ZIP_SCREENSHOT_WM_DIR = "preview_watermark_photo";
32
+ const SOURCE_PHOTO_DIR = "source_photo";
33
+ const ZIP_FILES_DIR = "zip";
34
+
35
+ const expandedName = ".jpg";
36
+
37
+ //并发数量
38
+ const queueCount = 50;
39
+
40
+ //起始任务下标
41
+ let taskIndex = 0;
42
+
43
+ const readExcel = (path) => {
44
+ const workbook = XLSX.readFile(path);
45
+
46
+ // 获取第一个工作表(Sheet)
47
+ const firstSheetName = workbook.SheetNames[0];
48
+ const worksheet = workbook.Sheets[firstSheetName];
49
+
50
+ // 将工作表转换为 JSON 对象
51
+ const jsonData = XLSX.utils.sheet_to_json(worksheet);
52
+ return jsonData;
53
+ };
54
+
55
+ function logFileSize(path) {
56
+ const fileSize = fs.statSync(path).size / (1024 * 1024);
57
+ const formattedSize = fileSize.toFixed(2); // 保留两位小数
58
+ logger(`图片大小:${formattedSize}M`);
59
+ }
60
+
61
+ //普通日志
62
+ const logger = (log, hash, err) => {
63
+ console.log(log);
64
+ fs.writeFileSync("log.txt", log + "\n", {
65
+ flag: "a",
66
+ });
67
+ };
68
+
69
+ //错误日志
70
+ const disposeError = (err) => {
71
+ logger("---任务失败---", err);
72
+ logger(
73
+ `******************************************************************end`
74
+ );
75
+ fs.writeFileSync("error.txt", err + "\n", {
76
+ flag: "a",
77
+ });
78
+ };
79
+
80
+ /**
81
+ * 递归遍历文件夹,查找文件
82
+ * @param {*} dir 文件夹路径
83
+ * @param {*} fileName 文件名称
84
+ * @returns string 匹配成功的文件路径
85
+ */
86
+ function findFileInDir(dir, fileName) {
87
+ const files = fs.readdirSync(dir);
88
+ for (const file of files) {
89
+ const filePath = path.join(dir, file);
90
+ const stat = fs.statSync(filePath);
91
+ if (stat.isDirectory()) {
92
+ const result = findFileInDir(filePath, fileName);
93
+ if (result) {
94
+ return result;
95
+ }
96
+ } else if (file.startsWith(fileName) && file.endsWith(expandedName)) {
97
+ return filePath;
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+
103
+ // function findFileInDir(dir, fileName) {
104
+ // return new Promise((resolve, reject) => {
105
+ // fs.readdir(dir, (err, files) => {
106
+ // if (err) {
107
+ // reject(err);
108
+ // return;
109
+ // }
110
+
111
+ // // 遍历目录中的文件和子目录
112
+ // for (const file of files) {
113
+ // const filePath = path.join(dir, file);
114
+ // fs.stat(filePath, (err, stat) => {
115
+ // if (err) {
116
+ // reject(err); // 如果获取文件状态出错,直接reject
117
+ // return;
118
+ // }
119
+ // if (stat.isDirectory()) {
120
+ // // 如果是目录,递归调用findFileInDir
121
+ // findFileInDir(filePath, fileName)
122
+ // .then(resolve) // 如果找到了目标文件,将结果resolve
123
+ // .catch(reject); // 如果在子目录中出错,将错误reject
124
+ // } else if (file.startsWith(fileName) && file.endsWith(expandedName)) {
125
+ // resolve(filePath); // 如果找到了目标文件,将结果resolve
126
+ // }
127
+ // });
128
+ // }
129
+
130
+ // // 如果遍历完了所有文件和子目录仍未找到目标文件,resolve一个null表示未找到
131
+ // resolve(null);
132
+ // });
133
+ // });
134
+ // }
135
+
136
+ /**
137
+ * 生成预览图 indexFilePath: output/0
138
+ * originFilePath E:\workspace\图片测试\photos\abc222.png
139
+ */
140
+ const PriviewPhoto = (indexFilePath, originFilePath, fileName) => {
141
+ //存放预览图图的文件夹
142
+ const outputDir = path.join(indexFilePath, SCREENSHOT_DIR);
143
+ ensureDirSync(outputDir);
144
+
145
+ const outputFile = path.join(outputDir, fileName);
146
+
147
+ return new Promise((resolve, reject) => {
148
+ const watermarkScreenshotsComand = ffmpeg(originFilePath);
149
+ watermarkScreenshotsComand
150
+ .size("1280x?")
151
+ .output(outputFile)
152
+ .on("error", (err) => {
153
+ logger("截图预览图出错: " + err);
154
+ reject();
155
+ })
156
+ .on("end", () => {
157
+ logger("预览图完成: " + outputFile);
158
+ resolve();
159
+ })
160
+ .run();
161
+ });
162
+ };
163
+
164
+ /**
165
+ * 生成水印预览图
166
+ */
167
+ const watermarkPriviewPhoto = (indexFilePath, originFilePath, fileName) => {
168
+ //存放水印截图的文件夹
169
+ const outputDir = path.join(indexFilePath, ZIP_SCREENSHOT_WM_DIR);
170
+ const inputDir = path.join(indexFilePath, SCREENSHOT_DIR, fileName);
171
+ ensureDirSync(outputDir);
172
+
173
+ const outputFile = path.join(outputDir, fileName);
174
+
175
+ return new Promise((resolve, reject) => {
176
+ const watermarkScreenshotsComand = ffmpeg(inputDir);
177
+ watermarkScreenshotsComand
178
+ .input(watermarkImage)
179
+ .complexFilter("overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2")
180
+ .output(outputFile)
181
+ .on("error", (err) => {
182
+ logger("截图添加水印出错: " + err);
183
+ reject();
184
+ })
185
+ .on("end", () => {
186
+ logger("截图水印添加完成: " + outputFile);
187
+ resolve();
188
+ })
189
+ .run();
190
+ });
191
+ };
192
+
193
+ /**
194
+ * 确保目录存在,如果不存在则创建它
195
+ */
196
+ function ensureDirSync(dirpath) {
197
+ try {
198
+ if (!fs.existsSync(dirpath)) {
199
+ fs.mkdirSync(dirpath, { recursive: true });
200
+ logger(`目录创建成功:${dirpath}`);
201
+ }
202
+ } catch (err) {
203
+ logger(`创建目录时出错:${err}`);
204
+ throw err;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * 打包物料
210
+ */
211
+ const archiveZip = (fileName, inputPath, originFilePath) => {
212
+ const zipDir = path.join(inputPath, "zip");
213
+ const timestamp = new Date().getTime();
214
+
215
+ ensureDirSync(zipDir);
216
+ const zipStream = fs.createWriteStream(
217
+ path.join(zipDir, `package${timestamp}.zip`)
218
+ );
219
+ const archive = archiver("zip", {
220
+ zlib: { level: 9 },
221
+ });
222
+
223
+ return new Promise((resolve, reject) => {
224
+ zipStream.on("close", function () {
225
+ logger("压缩数据:" + archive.pointer() + " total bytes");
226
+ logger(
227
+ "完成归档archiver has been finalized and the output file descriptor has closed."
228
+ );
229
+ resolve();
230
+ });
231
+
232
+ archive.on("warning", function (err) {
233
+ if (err.code === "ENOENT") {
234
+ logger("压缩-warning:" + err);
235
+ } else {
236
+ throw err;
237
+ }
238
+ });
239
+
240
+ archive.on("error", function (err) {
241
+ logger("压缩失败!" + err);
242
+ reject();
243
+ });
244
+
245
+ archive.pipe(zipStream);
246
+ const directories = [ZIP_SCREENSHOT_WM_DIR, SCREENSHOT_DIR];
247
+
248
+ directories.forEach((dir) => {
249
+ const dirPath = path.join(inputPath, dir);
250
+ archive.directory(dirPath, dir);
251
+ });
252
+
253
+ archive.file(originFilePath, {
254
+ name: path.join(SOURCE_PHOTO_DIR, fileName),
255
+ });
256
+ // 完成归档
257
+ archive.finalize();
258
+ });
259
+ };
260
+
261
+ /**
262
+ * 获取 元数据
263
+ */
264
+ const getMetadata = async (
265
+ fileName,
266
+ indexFilePath,
267
+ originFilePath,
268
+ { rowFileName, title, keyword }
269
+ ) => {
270
+ //源图片数据
271
+ const photoMetadataComand = ffmpeg(originFilePath);
272
+ //预览图数据
273
+ const photoInputPath = path.join(indexFilePath, SCREENSHOT_DIR, fileName);
274
+ const photoPreviewMetadataComand = ffmpeg(photoInputPath);
275
+
276
+ const metaDataParams = {
277
+ photo_id: rowFileName,
278
+ width: "",
279
+ height: "",
280
+ format: "",
281
+ size: "",
282
+ pr: 160,
283
+ dpi: 0,
284
+ is_government: 0,
285
+ common_width: "",
286
+ common_height: "",
287
+ dujia: 0,
288
+ xiaoxiang: 0,
289
+ title,
290
+ keyword,
291
+ en_keyword: "",
292
+ // preview_path: "",
293
+ // markImage_path: "",
294
+ // photo_path: "",
295
+ // common_image_path: "",
296
+ // en_keyword: "",
297
+ plate_id: 3,
298
+ transform_plate_id: 3,
299
+ tag_id: 47,
300
+ userid: 1003,
301
+ username: "美好景象",
302
+ demand_kind: 3,
303
+ source_from: 71,
304
+ file: "",
305
+ };
306
+
307
+ await new Promise((resolve, reject) => {
308
+ photoMetadataComand.ffprobe(function (err, metadata) {
309
+ // console.log("metadata");
310
+ // console.log(JSON.stringify(metadata));
311
+ if (metadata) {
312
+ const formatStream = metadata.format;
313
+ const photoStream = metadata.streams.find(
314
+ (s) => s.codec_type === "video"
315
+ );
316
+
317
+ metaDataParams.width = photoStream.width;
318
+ metaDataParams.height = photoStream.height;
319
+ // metaDataParams.format = formatStream.format_name;
320
+ metaDataParams.format = "jpg";
321
+ metaDataParams.size = formatStream.size;
322
+
323
+ resolve();
324
+ } else {
325
+ reject(err);
326
+ }
327
+ });
328
+ });
329
+ await new Promise((resolve, reject) => {
330
+ photoPreviewMetadataComand.ffprobe(function (err, metadata) {
331
+ if (metadata) {
332
+ const photoStream = metadata.streams.find(
333
+ (s) => s.codec_type === "video"
334
+ );
335
+
336
+ metaDataParams.common_width = photoStream.width;
337
+ metaDataParams.common_height = photoStream.height;
338
+ resolve();
339
+ } else {
340
+ reject(err);
341
+ }
342
+ });
343
+ });
344
+
345
+ return metaDataParams;
346
+ };
347
+
348
+ /**
349
+ * 接口
350
+ */
351
+ const postData = (dataParams, indexFilePath) => {
352
+ const formData = new FormData();
353
+
354
+ const zipFiles = fs
355
+ .readdirSync(path.join(indexFilePath, ZIP_FILES_DIR))
356
+ .find((file) => file.endsWith(".zip"));
357
+
358
+ const packageZip = path.join(indexFilePath, ZIP_FILES_DIR, zipFiles);
359
+
360
+ formData.append("file", fs.createReadStream(packageZip));
361
+ for (const key in dataParams) {
362
+ if (Object.hasOwnProperty.call(dataParams, key)) {
363
+ const value = dataParams[key];
364
+ formData.append(key, value);
365
+ }
366
+ }
367
+
368
+ logger("等待接口返回结果……");
369
+
370
+ // return axios.post("http://192.168.102.61:9999/upload/photo", formData, {
371
+ return axios.post("http://127.0.0.1:9999/upload/photo", formData, {
372
+ headers: {
373
+ "Content-Type": "multipart/form-data",
374
+ },
375
+ timeout: 300000,
376
+ });
377
+ };
378
+
379
+ /**
380
+ * 任务
381
+ */
382
+ const task = async (row, index) => {
383
+ logger(
384
+ "**************************" + row.fileName + "**************************"
385
+ );
386
+ // Excel的列名分别为: fileName title keyword
387
+ // 表格中文件名无后缀,遂手动添加写死为mp4
388
+ let fileName = row.fileName;
389
+ const rowFileName = row.fileName;
390
+ const title = row.title;
391
+ const keywordArr = row.keyword.split(",");
392
+ const filteredArray = keywordArr.filter((item) => item.trim() !== "");
393
+ const keyword = filteredArray.join(" ");
394
+
395
+ if (!fileName.includes(".")) {
396
+ fileName = row.fileName + expandedName;
397
+ }
398
+
399
+ const getPathStartTime = new Date();
400
+ // 源图片文件夹路径
401
+ const originFilePath = await findFileInDir(
402
+ path.join(workDir, ORIGIN_FILE_DIR),
403
+ fileName
404
+ );
405
+ const getPathEndTime = new Date();
406
+ const timeInSeconds = ((getPathEndTime - getPathStartTime) / 1000).toFixed(2);
407
+
408
+ logger(`第${index}条Path路径搜索时间:${timeInSeconds}秒`);
409
+ logger(`-------------------${new Date()}------------------`);
410
+
411
+ console.log("originFilePath:" + originFilePath);
412
+ console.log("fileName:" + fileName);
413
+
414
+ if (!originFilePath) {
415
+ logger(`图片文件 ${fileName} 不存在`);
416
+ return;
417
+ }
418
+
419
+ //index文件夹 output/0
420
+ const indexFilePath = path.join(workDir, "output", index + "");
421
+
422
+ try {
423
+ const workStartTime = new Date();
424
+ await fs_asnyc.access(originFilePath, fs_asnyc.constants.F_OK);
425
+ logFileSize(originFilePath);
426
+ await PriviewPhoto(indexFilePath, originFilePath, fileName);
427
+ await watermarkPriviewPhoto(indexFilePath, originFilePath, fileName);
428
+ await archiveZip(fileName, indexFilePath, originFilePath);
429
+ const dataParams = await getMetadata(
430
+ fileName,
431
+ indexFilePath,
432
+ originFilePath,
433
+ {
434
+ rowFileName,
435
+ title,
436
+ keyword,
437
+ }
438
+ );
439
+ const workEndTime = new Date();
440
+ const timeInSeconds = ((workEndTime - workStartTime) / 1000).toFixed(2);
441
+
442
+ logger(`第${index}条4090处理总时间:${timeInSeconds}秒`);
443
+ logger(`-------------------${new Date()}------------------`);
444
+
445
+ const postStartTime = new Date();
446
+ const resData = await postData(dataParams, indexFilePath);
447
+ const postEndTime = new Date();
448
+ const timeInSeconds1 = ((postEndTime - postStartTime) / 1000).toFixed(2);
449
+
450
+ logger(`第${index}条接口处理总时间:${timeInSeconds1}秒`);
451
+ logger(`-------------------${new Date()}------------------`);
452
+
453
+ if (resData.data.code === 200) {
454
+ logger("请求成功!");
455
+ logger(resData.data.code);
456
+ console.log(resData.data);
457
+ //删除文件夹
458
+ fs.rmdirSync(indexFilePath, { recursive: true });
459
+ } else {
460
+ throw new Error("请求失败!" + resData.data.msg);
461
+ }
462
+
463
+ logger(
464
+ `----------------------------------------第${index}条结束---------------------------------end`
465
+ );
466
+ } catch (error) {
467
+ // 可以约定code,来表示不同的错误信息
468
+ if (error.code === "ENOENT") {
469
+ logger(`图片文件 ${fileName} 不存在`);
470
+ } else {
471
+ logger("图片任务处理失败:" + error);
472
+ logger(error);
473
+ }
474
+ fs.rmdirSync(indexFilePath, { recursive: true });
475
+ disposeError(fileName);
476
+ }
477
+ };
478
+
479
+ /**
480
+ * 解析Excel数据
481
+ */
482
+ const transitionExcelToJSON = () => {
483
+ //当前任务hash
484
+ const hashOrigin = generateUniqueHash();
485
+ const hash = hashOrigin.slice(0, 8);
486
+ logger("图片批量任务任务开始---" + hash);
487
+ logger("当前目录: " + workDir);
488
+ logger("工作目录: " + __dirname);
489
+
490
+ const jsonData = readExcel(excelDir);
491
+
492
+ const run = (index) => {
493
+ try {
494
+ logger(
495
+ `run-------------------------------------第${index}条开始------------------------------------`
496
+ );
497
+ logger(`-------------------${new Date()}------------------`);
498
+ const row = jsonData[index];
499
+
500
+ if (!row) {
501
+ return;
502
+ }
503
+ task(row, index)
504
+ .then(() => {
505
+ taskIndex++;
506
+ run(taskIndex);
507
+ })
508
+ .catch(() => {
509
+ taskIndex++;
510
+ run(taskIndex);
511
+ });
512
+ } catch (error) {
513
+ logger("捕获错误!" + error);
514
+ taskIndex++;
515
+ run(taskIndex);
516
+ }
517
+ };
518
+
519
+ for (let i = 0; i < queueCount; i++) {
520
+ run(i + taskIndex);
521
+ }
522
+ };
523
+
524
+ transitionExcelToJSON();
@@ -517,9 +517,6 @@ const transitionExcelToJSON = () => {
517
517
  }
518
518
  };
519
519
 
520
- for (let i = jsonData.length; i > 0; i--) {
521
- run(i);
522
- }
523
520
  for (let i = 0; i < queueCount; i++) {
524
521
  run(taskIndex - i);
525
522
  }