tools_batch_files 1.0.36 → 1.0.37
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/package.json +2 -1
- package/src/batchTash.js +44 -0
- package/src/photoFn/photoBatch.js +560 -0
- package/src/queue.js +519 -0
- package/src/videoFn/videoBatch.js +696 -696
package/src/queue.js
ADDED
@@ -0,0 +1,519 @@
|
|
1
|
+
const fs = require("fs");
|
2
|
+
const path = require("path");
|
3
|
+
const XLSX = require("xlsx");
|
4
|
+
const ini = require("ini");
|
5
|
+
|
6
|
+
class Queue {
|
7
|
+
/**
|
8
|
+
* 数据信息文件地址
|
9
|
+
*/
|
10
|
+
excelPath = "";
|
11
|
+
/**
|
12
|
+
* 工作目录
|
13
|
+
*/
|
14
|
+
workDir = "";
|
15
|
+
/**
|
16
|
+
* 数据源
|
17
|
+
*/
|
18
|
+
originData = [];
|
19
|
+
/**
|
20
|
+
* 队列
|
21
|
+
*/
|
22
|
+
queue = [];
|
23
|
+
/**
|
24
|
+
* 最大并发队列数量
|
25
|
+
*/
|
26
|
+
queueMaxCount = 5;
|
27
|
+
/**
|
28
|
+
* 队列索引
|
29
|
+
*/
|
30
|
+
queueIndex = 0;
|
31
|
+
/**
|
32
|
+
* 队列运行过程
|
33
|
+
*/
|
34
|
+
onProcess = null;
|
35
|
+
/**
|
36
|
+
* 任务完成
|
37
|
+
*/
|
38
|
+
onComplete = null;
|
39
|
+
/**
|
40
|
+
* 更新表格队列
|
41
|
+
*/
|
42
|
+
queueExcel = [];
|
43
|
+
/**
|
44
|
+
* 配置文件
|
45
|
+
*/
|
46
|
+
config;
|
47
|
+
constructor(props) {
|
48
|
+
this.excelPath = props.excelPath;
|
49
|
+
this.workDir = props.workDir;
|
50
|
+
this.onProcess = props.onProcess;
|
51
|
+
this.onComplete = props.onComplete;
|
52
|
+
|
53
|
+
this.init();
|
54
|
+
}
|
55
|
+
|
56
|
+
init() {
|
57
|
+
const that = this;
|
58
|
+
const { excelPath } = that;
|
59
|
+
|
60
|
+
//创建配置文件
|
61
|
+
this.config = this.createConfig();
|
62
|
+
|
63
|
+
//创建临时的数据文件
|
64
|
+
if (!this.config.tmpFile) {
|
65
|
+
this.createTepExcel(excelPath);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* 监听事件
|
71
|
+
*/
|
72
|
+
event() {
|
73
|
+
const that = this;
|
74
|
+
/**
|
75
|
+
* 监听异常退出
|
76
|
+
*/
|
77
|
+
process.on("uncaughtException", () => {
|
78
|
+
that.config.status = "error";
|
79
|
+
});
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* 创建配置文件
|
84
|
+
*/
|
85
|
+
createConfig() {
|
86
|
+
let config;
|
87
|
+
if (!this.isConfigExist()) {
|
88
|
+
config = {
|
89
|
+
/**
|
90
|
+
* @param {string} done 主列表数据完成
|
91
|
+
* @param {string} end 任务结束
|
92
|
+
*/
|
93
|
+
status: "",
|
94
|
+
/**
|
95
|
+
* 总任务数量
|
96
|
+
*/
|
97
|
+
totalCount: 0,
|
98
|
+
/**
|
99
|
+
* 成功任务数量
|
100
|
+
*/
|
101
|
+
successCount: 0,
|
102
|
+
/**
|
103
|
+
* 临时文件地址
|
104
|
+
*/
|
105
|
+
tmpFile: "",
|
106
|
+
/**
|
107
|
+
* 重试次数
|
108
|
+
*/
|
109
|
+
retryCount: 0,
|
110
|
+
};
|
111
|
+
const configString = ini.stringify(config);
|
112
|
+
this.writeWorkFile("config.ini", configString);
|
113
|
+
} else {
|
114
|
+
config = ini.parse(this.readWorkFile("config.ini"));
|
115
|
+
//重置次数
|
116
|
+
config.retryCount = 0;
|
117
|
+
config.successCount = parseInt(config.successCount);
|
118
|
+
config.totalCount = parseInt(config.totalCount);
|
119
|
+
}
|
120
|
+
return config;
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* 运行队列
|
125
|
+
*/
|
126
|
+
async start() {
|
127
|
+
const that = this;
|
128
|
+
|
129
|
+
//读取数据-异常时从错误列表中读取
|
130
|
+
const excelData = this.getExcelData();
|
131
|
+
this.queueData = excelData;
|
132
|
+
|
133
|
+
//跑老数据不用同步数量
|
134
|
+
if (!that.config.totalCount) {
|
135
|
+
//记录总任务数量
|
136
|
+
const count = excelData.length;
|
137
|
+
that.config.totalCount = count - 1;
|
138
|
+
that.setConfig("totalCount", count);
|
139
|
+
}
|
140
|
+
|
141
|
+
//运行程序
|
142
|
+
this.run();
|
143
|
+
}
|
144
|
+
|
145
|
+
/**
|
146
|
+
* 重启任务
|
147
|
+
*/
|
148
|
+
reStart() {
|
149
|
+
//读取数据-异常时从错误列表中读取
|
150
|
+
const excelData = this.getExcelData();
|
151
|
+
this.queueData = excelData;
|
152
|
+
|
153
|
+
//运行程序
|
154
|
+
this.run();
|
155
|
+
}
|
156
|
+
|
157
|
+
/**
|
158
|
+
* 默认运行
|
159
|
+
*/
|
160
|
+
run() {
|
161
|
+
const that = this;
|
162
|
+
this.createQueue().then(() => {
|
163
|
+
//如果没有错误文件任务结束否则重启
|
164
|
+
if (
|
165
|
+
that.config.successCount < that.config.totalCount &&
|
166
|
+
that.config.retryCount <= 5
|
167
|
+
) {
|
168
|
+
that.config.retryCount++;
|
169
|
+
that.setConfig("retryCount", that.config.retryCount);
|
170
|
+
|
171
|
+
that.reStart();
|
172
|
+
} else {
|
173
|
+
that.config.status = "done";
|
174
|
+
that.setConfig("status", "done");
|
175
|
+
|
176
|
+
//全部执行完毕
|
177
|
+
if (that.config.successCount === that.config.totalCount) {
|
178
|
+
//归档配置文件
|
179
|
+
that.renameConfig();
|
180
|
+
//归档临时文件
|
181
|
+
that.renameTempExcel();
|
182
|
+
}
|
183
|
+
|
184
|
+
//队列执行完成
|
185
|
+
that.onComplete?.();
|
186
|
+
}
|
187
|
+
});
|
188
|
+
}
|
189
|
+
|
190
|
+
/**
|
191
|
+
* 单个任务
|
192
|
+
*/
|
193
|
+
singleTask(nextData, resolve) {
|
194
|
+
const that = this;
|
195
|
+
const { primaryKey } = that;
|
196
|
+
const id = nextData[primaryKey] || nextData["queue_id"];
|
197
|
+
|
198
|
+
that
|
199
|
+
.onProcess(nextData)
|
200
|
+
.then(() => {
|
201
|
+
that.config.successCount++;
|
202
|
+
that.setConfig("successCount", that.config.successCount);
|
203
|
+
|
204
|
+
that.updateExcel(id, 1);
|
205
|
+
that.queueNext(resolve);
|
206
|
+
})
|
207
|
+
.catch(() => {
|
208
|
+
that.queueNext(resolve);
|
209
|
+
//处理错误
|
210
|
+
});
|
211
|
+
}
|
212
|
+
|
213
|
+
/**
|
214
|
+
* 创建上传队列
|
215
|
+
*/
|
216
|
+
createQueue() {
|
217
|
+
const queueList = [];
|
218
|
+
for (let i = 0; i < this.queueMaxCount; i++) {
|
219
|
+
queueList.push(
|
220
|
+
new Promise((resolve) => {
|
221
|
+
this.queueNext(resolve);
|
222
|
+
})
|
223
|
+
);
|
224
|
+
|
225
|
+
this.queueIndex++;
|
226
|
+
}
|
227
|
+
|
228
|
+
return Promise.all(queueList);
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* 队列运行后,继续下一个任务
|
233
|
+
*/
|
234
|
+
queueNext(resolve) {
|
235
|
+
const next = this.dequeue();
|
236
|
+
if (next) {
|
237
|
+
this.singleTask(next, resolve);
|
238
|
+
} else {
|
239
|
+
//当前队列全部结束
|
240
|
+
resolve();
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
//移出队列
|
245
|
+
dequeue() {
|
246
|
+
return this.queueData.shift();
|
247
|
+
}
|
248
|
+
|
249
|
+
/**
|
250
|
+
* 获取表格数据
|
251
|
+
*/
|
252
|
+
getExcelData() {
|
253
|
+
const excelPath = this.config.tmpFile;
|
254
|
+
if (!fs.existsSync(excelPath)) {
|
255
|
+
throw new Error("Excel file not found");
|
256
|
+
}
|
257
|
+
return this.readExcel(excelPath);
|
258
|
+
}
|
259
|
+
|
260
|
+
/**
|
261
|
+
* 更新表格
|
262
|
+
*/
|
263
|
+
updateExcel(key, value) {
|
264
|
+
this.queueExcel.push([key, value]);
|
265
|
+
if (this.updateExcelStatus !== "process") {
|
266
|
+
this.updateExcelStatus = "process";
|
267
|
+
this.runQueueExcel();
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
/**
|
272
|
+
* 归档临时文件
|
273
|
+
*/
|
274
|
+
renameTempExcel() {
|
275
|
+
const that = this;
|
276
|
+
const { tmpFile } = that.config;
|
277
|
+
const localDate = that.getCurrentDateTimeWithSlashes();
|
278
|
+
const originPath = tmpFile;
|
279
|
+
const targetPath = tmpFile + "." + localDate + ".bak";
|
280
|
+
|
281
|
+
fs.rename(originPath, targetPath, (err) => {
|
282
|
+
if (err) {
|
283
|
+
console.error("文件重命名失败:", err);
|
284
|
+
}
|
285
|
+
});
|
286
|
+
}
|
287
|
+
/**
|
288
|
+
* 归档配置文件
|
289
|
+
*/
|
290
|
+
renameConfig() {
|
291
|
+
const that = this;
|
292
|
+
const localDate = that.getCurrentDateTimeWithSlashes();
|
293
|
+
const originPath = path.join(that.workDir, "config.ini");
|
294
|
+
const targetPath = path.join(
|
295
|
+
that.workDir,
|
296
|
+
"config.ini." + localDate + ".bak"
|
297
|
+
);
|
298
|
+
|
299
|
+
fs.rename(originPath, targetPath, (err) => {
|
300
|
+
if (err) {
|
301
|
+
console.error("文件重命名失败:", err);
|
302
|
+
}
|
303
|
+
});
|
304
|
+
}
|
305
|
+
|
306
|
+
/**
|
307
|
+
* 时间补足
|
308
|
+
* @returns
|
309
|
+
*/
|
310
|
+
getCurrentDateTimeWithSlashes() {
|
311
|
+
const now = new Date();
|
312
|
+
const year = now.getFullYear();
|
313
|
+
const month = String(now.getMonth() + 1).padStart(2, "0"); // 月份是从0开始的,所以加1
|
314
|
+
const day = String(now.getDate()).padStart(2, "0"); // 日期不足两位前补0
|
315
|
+
const hours = String(now.getHours()).padStart(2, "0"); // 时不足两位前补0
|
316
|
+
const minutes = String(now.getMinutes()).padStart(2, "0"); // 分不足两位前补0
|
317
|
+
const seconds = String(now.getSeconds()).padStart(2, "0"); // 秒不足两位前补0
|
318
|
+
|
319
|
+
return `${year}-${month}-${day}-${hours}-${minutes}-${seconds}`;
|
320
|
+
}
|
321
|
+
|
322
|
+
/**
|
323
|
+
* 运行数据更新
|
324
|
+
*/
|
325
|
+
runQueueExcel() {
|
326
|
+
const that = this;
|
327
|
+
const value = this.queueExcel.shift();
|
328
|
+
if (value) {
|
329
|
+
that.updateExcelFile(...value).then(() => {
|
330
|
+
that.runQueueExcel();
|
331
|
+
});
|
332
|
+
} else {
|
333
|
+
that.updateExcelStatus = "done";
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
/**
|
338
|
+
*
|
339
|
+
* @param {string} key 主键名称
|
340
|
+
* @param {string} value status的值
|
341
|
+
*/
|
342
|
+
updateExcelFile(id, status) {
|
343
|
+
return new Promise((resolve) => {
|
344
|
+
const workbook = XLSX.readFile(this.config.tmpFile);
|
345
|
+
// 获取第一个工作表
|
346
|
+
const sheetName = workbook.Sheets[workbook.SheetNames[0]];
|
347
|
+
const idColumnName = this.primaryKey || "queue_id"; // 假设 'ID' 列包含了唯一标识符
|
348
|
+
const idIndex = findColumnIndexByName(sheetName, idColumnName);
|
349
|
+
|
350
|
+
// 查找列索引
|
351
|
+
function findColumnIndexByName(sheet, columnName) {
|
352
|
+
const firstRow = 0; // 假设第一行是标题行
|
353
|
+
const range = XLSX.utils.decode_range(sheet["!ref"]);
|
354
|
+
for (let col = 0; col <= range.e.c; col++) {
|
355
|
+
const cellAddress = XLSX.utils.encode_cell({ r: firstRow, c: col });
|
356
|
+
const cell = sheet[cellAddress];
|
357
|
+
if (cell && cell.v === columnName) {
|
358
|
+
return col;
|
359
|
+
}
|
360
|
+
}
|
361
|
+
return -1; // 如果没有找到返回 -1
|
362
|
+
}
|
363
|
+
// 查找具有特定ID的行
|
364
|
+
function findRowById(sheet, value, idIndex) {
|
365
|
+
const range = XLSX.utils.decode_range(sheet["!ref"]);
|
366
|
+
for (let row = 1; row <= range.e.r; row++) {
|
367
|
+
const cellAddress = XLSX.utils.encode_cell({ r: row, c: idIndex }); // 假设ID在第一列
|
368
|
+
const cell = sheet[cellAddress];
|
369
|
+
if (cell && cell.v === value) {
|
370
|
+
return row;
|
371
|
+
}
|
372
|
+
}
|
373
|
+
return null; // 如果没有找到返回 null
|
374
|
+
}
|
375
|
+
|
376
|
+
const statusIndex = findColumnIndexByName(sheetName, "status");
|
377
|
+
const rowIndex = findRowById(sheetName, id, idIndex);
|
378
|
+
if (rowIndex !== null) {
|
379
|
+
// 更新同一行的其他列
|
380
|
+
const cellAddress = XLSX.utils.encode_cell({
|
381
|
+
r: rowIndex,
|
382
|
+
c: statusIndex,
|
383
|
+
});
|
384
|
+
sheetName[cellAddress].v = status;
|
385
|
+
|
386
|
+
// 写入更新后的文件
|
387
|
+
XLSX.writeFile(workbook, this.config.tmpFile);
|
388
|
+
|
389
|
+
resolve();
|
390
|
+
}
|
391
|
+
resolve();
|
392
|
+
});
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* 创建临时的excel
|
397
|
+
* @param {*} excelPath Excel路径
|
398
|
+
* @param {*} isKey 主键名
|
399
|
+
*/
|
400
|
+
createTepExcel(excelPath, isKey) {
|
401
|
+
const workDir = this.workDir;
|
402
|
+
const fileName = excelPath.match(/[^/]+$/)[0].split(".")[0];
|
403
|
+
const tmpFile = workDir + "/" + fileName + ".tmp" + ".xlsx";
|
404
|
+
// 读取现有工作簿
|
405
|
+
const workbook = XLSX.readFile(excelPath);
|
406
|
+
|
407
|
+
const firstSheetName = workbook.SheetNames[0];
|
408
|
+
const worksheet = workbook.Sheets[firstSheetName];
|
409
|
+
|
410
|
+
const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
411
|
+
|
412
|
+
//增加数据
|
413
|
+
for (let i = 0; i < data.length; i++) {
|
414
|
+
const _data = data[i];
|
415
|
+
if (i === 0) {
|
416
|
+
//添加状态列
|
417
|
+
_data.push("status");
|
418
|
+
|
419
|
+
//添加主键列
|
420
|
+
if (!isKey) {
|
421
|
+
_data.push("queue_id");
|
422
|
+
}
|
423
|
+
} else {
|
424
|
+
_data.push(0);
|
425
|
+
if (!isKey) {
|
426
|
+
_data.push(fileName + "_" + i);
|
427
|
+
}
|
428
|
+
}
|
429
|
+
}
|
430
|
+
|
431
|
+
// 合并已有数据和新数据
|
432
|
+
const combinedData = data;
|
433
|
+
// 创建新的 sheet
|
434
|
+
const newWs = XLSX.utils.aoa_to_sheet(combinedData);
|
435
|
+
|
436
|
+
// 创建新的 workbook
|
437
|
+
const wb = XLSX.utils.book_new();
|
438
|
+
|
439
|
+
// 将新的 sheet 添加到 workbook
|
440
|
+
XLSX.utils.book_append_sheet(wb, newWs, "sheet1");
|
441
|
+
|
442
|
+
// 写入新工作簿
|
443
|
+
XLSX.writeFile(wb, tmpFile);
|
444
|
+
this.setConfig("tmpFile", tmpFile);
|
445
|
+
}
|
446
|
+
|
447
|
+
/**
|
448
|
+
* 读取Excel
|
449
|
+
* @param {*} xlsxPath Excel路径
|
450
|
+
* @returns JSON
|
451
|
+
*/
|
452
|
+
readExcel(xlsxPath) {
|
453
|
+
const workbook = XLSX.readFile(xlsxPath);
|
454
|
+
|
455
|
+
// 获取第一个工作表(Sheet)
|
456
|
+
const firstSheetName = workbook.SheetNames[0];
|
457
|
+
const worksheet = workbook.Sheets[firstSheetName];
|
458
|
+
|
459
|
+
// 将工作表转换为 JSON 对象
|
460
|
+
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
461
|
+
|
462
|
+
//过滤未上传的id
|
463
|
+
const filterData = jsonData.filter((item) => {
|
464
|
+
return item.status === 0;
|
465
|
+
});
|
466
|
+
|
467
|
+
return filterData;
|
468
|
+
}
|
469
|
+
|
470
|
+
/**
|
471
|
+
* 判断是否已经有配置文件
|
472
|
+
*/
|
473
|
+
isConfigExist() {
|
474
|
+
const configPath = path.join(this.workDir, "config.ini");
|
475
|
+
return fs.existsSync(configPath);
|
476
|
+
}
|
477
|
+
|
478
|
+
/**
|
479
|
+
* 更新配置文件
|
480
|
+
*/
|
481
|
+
setConfig(key, value) {
|
482
|
+
//更新内存中config
|
483
|
+
this.config[key] = value;
|
484
|
+
const configString = ini.stringify(this.config);
|
485
|
+
//更新配置文件
|
486
|
+
this.writeWorkFile("config.ini", configString);
|
487
|
+
}
|
488
|
+
/**
|
489
|
+
* 读取工作目录文件
|
490
|
+
* @param {string} fileName 文件名称
|
491
|
+
* @returns
|
492
|
+
*/
|
493
|
+
readWorkFile(fileName) {
|
494
|
+
const path = this.workDir;
|
495
|
+
const content = fs.readFileSync(path + "/" + fileName, "utf8");
|
496
|
+
return content;
|
497
|
+
}
|
498
|
+
|
499
|
+
/**
|
500
|
+
* 创建文件到工作目录
|
501
|
+
*/
|
502
|
+
writeWorkFile(fileName, content) {
|
503
|
+
const path = this.workDir;
|
504
|
+
fs.writeFileSync(path + "/" + fileName, content);
|
505
|
+
}
|
506
|
+
|
507
|
+
/**
|
508
|
+
* 复制文件
|
509
|
+
*/
|
510
|
+
copyFile(source, destination) {
|
511
|
+
fs.copyFileSync(source, destination);
|
512
|
+
}
|
513
|
+
|
514
|
+
isEmpty() {
|
515
|
+
return this.queue.length === 0;
|
516
|
+
}
|
517
|
+
}
|
518
|
+
|
519
|
+
exports.Queue = Queue;
|