tools_batch_files 1.0.28 → 1.0.30
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 +618 -618
- package/package.json +2 -1
- package/src/audioFn/audioBatch.js +440 -0
- package/utils/logger.js +48 -0
- package/utils/settleFiles.js +58 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "tools_batch_files",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.30",
|
4
4
|
"description": "批处理视频工具",
|
5
5
|
"keywords": [
|
6
6
|
"utils",
|
@@ -12,6 +12,7 @@
|
|
12
12
|
"bin": {
|
13
13
|
"tbf": "./index.js",
|
14
14
|
"tbfp": "./src/photoBatch.js",
|
15
|
+
"tbfa": "./src/audioFn/audioBatch.js",
|
15
16
|
"tbfb": "./src/photoBatchBack.js",
|
16
17
|
"tbfre": "./src/removeFailVideo.js"
|
17
18
|
},
|
@@ -0,0 +1,440 @@
|
|
1
|
+
const generateUniqueHash = require("../utils/index");
|
2
|
+
const { disposeError, readExcel, logger } = require("../utils/logger");
|
3
|
+
|
4
|
+
const {
|
5
|
+
findFileInDir,
|
6
|
+
logFileSize,
|
7
|
+
ensureDirSync,
|
8
|
+
} = require("../utils/settleFiles");
|
9
|
+
const ffmpeg = require("fluent-ffmpeg");
|
10
|
+
const fs = require("fs");
|
11
|
+
const path = require("path");
|
12
|
+
const fs_asnyc = require("fs").promises;
|
13
|
+
const archiver = require("archiver");
|
14
|
+
const axios = require("axios");
|
15
|
+
const FormData = require("form-data");
|
16
|
+
|
17
|
+
//并发数量
|
18
|
+
const queueCount = 1;
|
19
|
+
|
20
|
+
//起始任务下标
|
21
|
+
let taskIndex = 0;
|
22
|
+
|
23
|
+
//执行目录
|
24
|
+
const exeDir = __dirname;
|
25
|
+
//当前工作目录
|
26
|
+
const workDir = process.cwd();
|
27
|
+
// 表格目录
|
28
|
+
const excelDir = path.join(workDir, "excel", "excel.xlsx");
|
29
|
+
|
30
|
+
const ORIGIN_FILE_DIR = "audio";
|
31
|
+
const WM_AUDIO_DIR = "watermark_audio";
|
32
|
+
const WAV_AUDIO_DIR = "wav_audio";
|
33
|
+
const SOURCE_AUDIO_DIR = "source_audio";
|
34
|
+
const ZIP_FILES_DIR = "zip";
|
35
|
+
//拷贝 - 存放上传失败的文件
|
36
|
+
const Error_Files_Dir = "failed_audio";
|
37
|
+
//水印
|
38
|
+
const watermarkAudioPath = path.join(__dirname, "..", "vocal_print", "mz.mp3");
|
39
|
+
const longWatermarkAudioPath = path.join(
|
40
|
+
__dirname,
|
41
|
+
"..",
|
42
|
+
"vocal_print",
|
43
|
+
"800a.mp3"
|
44
|
+
);
|
45
|
+
|
46
|
+
const maxRetries = 1; // 最大重试次数
|
47
|
+
let retryCount = 0; // 当前重试次数
|
48
|
+
|
49
|
+
/**
|
50
|
+
* 声纹
|
51
|
+
*/
|
52
|
+
const watermarkAudio = (indexFilePath, originFilePath, fileName, duration) => {
|
53
|
+
//存放声纹文件夹
|
54
|
+
const outputDir = path.join(indexFilePath, WM_AUDIO_DIR);
|
55
|
+
const outputFile = path.join(outputDir, fileName);
|
56
|
+
let vocalPath = "";
|
57
|
+
|
58
|
+
ensureDirSync(outputDir);
|
59
|
+
logger("duration" + duration);
|
60
|
+
|
61
|
+
// 确保输出时长与源音频保持一致,方案改为截取声纹音频了。
|
62
|
+
const complexFilter = [
|
63
|
+
`[0:a]volume=1[a0]`, // 设置原音频音量为1,重命名为a0
|
64
|
+
`[1:a]atrim=0:duration=${duration},volume=1.5[a1]`, // 对声纹音频流进行截取操作,并设置音量,重命名为a1
|
65
|
+
`[a0][a1]amix=inputs=2[a]`, // 合并两个音频流为一个输出流,重命名为a
|
66
|
+
`[a]volume=3.0`, // 设置输出音频的音量
|
67
|
+
].join(";");
|
68
|
+
|
69
|
+
if (duration <= 5) {
|
70
|
+
//直接合并,放在末尾,下次补上
|
71
|
+
vocalPath = watermarkAudioPath;
|
72
|
+
} else if (duration > 5 && duration < 30) {
|
73
|
+
// 5s开始/ 每隔10s出现一次
|
74
|
+
vocalPath = watermarkAudioPath;
|
75
|
+
|
76
|
+
const totalCount = Math.floor((duration + 5) / 10);
|
77
|
+
for (let i = 1; i <= totalCount; i++) {
|
78
|
+
if (i === 1) {
|
79
|
+
complexFilter.push(`[1:a]adelay=5000|2000,volume=2.0[a${i}]`);
|
80
|
+
} else {
|
81
|
+
complexFilter.push(
|
82
|
+
`[1:a]adelay=${5000 + 10000 * (i - 1)}|2000,volume=2.0[a${i}]`
|
83
|
+
);
|
84
|
+
}
|
85
|
+
}
|
86
|
+
} else {
|
87
|
+
//30以上 10s开始 每隔10s出现一次
|
88
|
+
vocalPath = longWatermarkAudioPath;
|
89
|
+
}
|
90
|
+
|
91
|
+
return new Promise((resolve, reject) => {
|
92
|
+
const watermarkCommand = ffmpeg();
|
93
|
+
// 打声纹
|
94
|
+
watermarkCommand
|
95
|
+
.input(originFilePath)
|
96
|
+
.input(vocalPath) // 声纹音频文件
|
97
|
+
|
98
|
+
.complexFilter(complexFilter)
|
99
|
+
.output(outputFile)
|
100
|
+
.on("error", (err) => {
|
101
|
+
logger("添加声纹出错: " + err);
|
102
|
+
reject(err);
|
103
|
+
})
|
104
|
+
.on("end", () => {
|
105
|
+
logger("添加声纹完成: " + outputFile);
|
106
|
+
resolve();
|
107
|
+
})
|
108
|
+
.run();
|
109
|
+
});
|
110
|
+
};
|
111
|
+
|
112
|
+
/**
|
113
|
+
* 生成wav
|
114
|
+
*/
|
115
|
+
const wavAudio = (indexFilePath, originFilePath, fileName) => {
|
116
|
+
//存放wav文件夹
|
117
|
+
const outputDir = path.join(indexFilePath, WAV_AUDIO_DIR);
|
118
|
+
ensureDirSync(outputDir);
|
119
|
+
|
120
|
+
const outputFile = path.join(outputDir, fileName.replace(".mp3", ".wav"));
|
121
|
+
|
122
|
+
return new Promise((resolve, reject) => {
|
123
|
+
const watermarkCommand = ffmpeg(originFilePath);
|
124
|
+
watermarkCommand
|
125
|
+
.output(outputFile)
|
126
|
+
.on("error", (err) => {
|
127
|
+
logger("生成wav出错: " + err);
|
128
|
+
reject();
|
129
|
+
})
|
130
|
+
.on("end", () => {
|
131
|
+
logger("wav文件生成完成: " + outputFile);
|
132
|
+
resolve();
|
133
|
+
})
|
134
|
+
.run();
|
135
|
+
});
|
136
|
+
};
|
137
|
+
|
138
|
+
/**
|
139
|
+
* 打包物料
|
140
|
+
*/
|
141
|
+
const archiveZip = (fileName, inputPath, originFilePath) => {
|
142
|
+
const zipDir = path.join(inputPath, "zip");
|
143
|
+
const timestamp = new Date().getTime();
|
144
|
+
|
145
|
+
ensureDirSync(zipDir);
|
146
|
+
const zipStream = fs.createWriteStream(
|
147
|
+
path.join(zipDir, `package${timestamp}.zip`)
|
148
|
+
);
|
149
|
+
const archive = archiver("zip", {
|
150
|
+
zlib: { level: 9 },
|
151
|
+
});
|
152
|
+
|
153
|
+
return new Promise((resolve, reject) => {
|
154
|
+
zipStream.on("close", function () {
|
155
|
+
logger("压缩数据:" + archive.pointer() + " total bytes");
|
156
|
+
logger(
|
157
|
+
"完成归档archiver has been finalized and the output file descriptor has closed."
|
158
|
+
);
|
159
|
+
resolve();
|
160
|
+
});
|
161
|
+
|
162
|
+
archive.on("warning", function (err) {
|
163
|
+
if (err.code === "ENOENT") {
|
164
|
+
logger("压缩-warning:" + err);
|
165
|
+
} else {
|
166
|
+
throw err;
|
167
|
+
}
|
168
|
+
});
|
169
|
+
|
170
|
+
archive.on("error", function (err) {
|
171
|
+
logger("压缩失败!" + err);
|
172
|
+
reject();
|
173
|
+
});
|
174
|
+
|
175
|
+
archive.pipe(zipStream);
|
176
|
+
const directories = [WAV_AUDIO_DIR, WM_AUDIO_DIR];
|
177
|
+
|
178
|
+
directories.forEach((dir) => {
|
179
|
+
const dirPath = path.join(inputPath, dir);
|
180
|
+
archive.directory(dirPath, dir);
|
181
|
+
});
|
182
|
+
|
183
|
+
archive.file(originFilePath, {
|
184
|
+
name: path.join(SOURCE_AUDIO_DIR, fileName),
|
185
|
+
});
|
186
|
+
// 完成归档
|
187
|
+
archive.finalize();
|
188
|
+
});
|
189
|
+
};
|
190
|
+
|
191
|
+
/**
|
192
|
+
* 获取 元数据
|
193
|
+
*/
|
194
|
+
const getMetadata = async (
|
195
|
+
fileName,
|
196
|
+
indexFilePath,
|
197
|
+
originFilePath,
|
198
|
+
{ rowFileName, title, keyword }
|
199
|
+
) => {
|
200
|
+
//源音频数据
|
201
|
+
const photoMetadataComand = ffmpeg(originFilePath);
|
202
|
+
|
203
|
+
const metaDataParams = {
|
204
|
+
userid: 192375294,
|
205
|
+
username: "张杰",
|
206
|
+
title,
|
207
|
+
keyword,
|
208
|
+
en_keyword: "",
|
209
|
+
en_title: "",
|
210
|
+
size: "",
|
211
|
+
pr: 0,
|
212
|
+
sampling: "", // 采样率
|
213
|
+
duration: "",
|
214
|
+
tag_ids: "3",
|
215
|
+
source_from: "sound_1",
|
216
|
+
};
|
217
|
+
|
218
|
+
await new Promise((resolve, reject) => {
|
219
|
+
photoMetadataComand.ffprobe(function (err, metadata) {
|
220
|
+
if (metadata) {
|
221
|
+
const formatStream = metadata.format;
|
222
|
+
const audioStream = metadata.streams.find(
|
223
|
+
(s) => s.codec_type === "audio"
|
224
|
+
);
|
225
|
+
|
226
|
+
metaDataParams.size = formatStream.size;
|
227
|
+
metaDataParams.duration = audioStream.duration;
|
228
|
+
metaDataParams.sampling = audioStream.sample_rate;
|
229
|
+
|
230
|
+
resolve();
|
231
|
+
} else {
|
232
|
+
reject(err);
|
233
|
+
}
|
234
|
+
});
|
235
|
+
});
|
236
|
+
|
237
|
+
return metaDataParams;
|
238
|
+
};
|
239
|
+
|
240
|
+
/**
|
241
|
+
* 接口
|
242
|
+
*/
|
243
|
+
const postData = (dataParams, indexFilePath) => {
|
244
|
+
const formData = new FormData();
|
245
|
+
|
246
|
+
const zipFiles = fs
|
247
|
+
.readdirSync(path.join(indexFilePath, ZIP_FILES_DIR))
|
248
|
+
.find((file) => file.endsWith(".zip"));
|
249
|
+
|
250
|
+
const packageZip = path.join(indexFilePath, ZIP_FILES_DIR, zipFiles);
|
251
|
+
|
252
|
+
formData.append("file", fs.createReadStream(packageZip));
|
253
|
+
for (const key in dataParams) {
|
254
|
+
if (Object.hasOwnProperty.call(dataParams, key)) {
|
255
|
+
const value = dataParams[key];
|
256
|
+
formData.append(key, value);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
logger("等待接口返回结果……");
|
261
|
+
|
262
|
+
// return axios.post("http://127.0.0.1:9999/upload/photo", formData, {
|
263
|
+
// return axios.post("http://192.168.102.61:9999/upload/sound", formData, {
|
264
|
+
return axios.post("http://192.168.101.149:9999/upload/sound", formData, {
|
265
|
+
headers: {
|
266
|
+
"Content-Type": "multipart/form-data",
|
267
|
+
},
|
268
|
+
timeout: 300000,
|
269
|
+
});
|
270
|
+
};
|
271
|
+
|
272
|
+
/**
|
273
|
+
* 接口重试机制
|
274
|
+
*/
|
275
|
+
async function postDataWithRetry(dataParams, indexFilePath) {
|
276
|
+
while (retryCount < maxRetries) {
|
277
|
+
try {
|
278
|
+
const resData = await postData(dataParams, indexFilePath);
|
279
|
+
if (resData.data.code === 200) {
|
280
|
+
logger("请求成功!");
|
281
|
+
logger(resData.data);
|
282
|
+
fs.rmdirSync(indexFilePath, { recursive: true });
|
283
|
+
return; // 请求成功,结束循环
|
284
|
+
} else if (resData.data.code === 300) {
|
285
|
+
// 重复上传,不捕获此错误
|
286
|
+
logger(`第${index}条文件-${index}重复上传!`);
|
287
|
+
fs.rmdirSync(indexFilePath, { recursive: true });
|
288
|
+
return; // 结束循环
|
289
|
+
} else {
|
290
|
+
// 其他错误,抛出异常
|
291
|
+
throw new Error("请求失败!" + resData.data);
|
292
|
+
}
|
293
|
+
} catch (error) {
|
294
|
+
logger(`请求失败,重试中... (${retryCount + 1}/${maxRetries})`);
|
295
|
+
logger(error);
|
296
|
+
retryCount++;
|
297
|
+
// 延时等待一段时间后再进行重试
|
298
|
+
await new Promise((resolve) => setTimeout(resolve, 100)); // 等待1秒
|
299
|
+
}
|
300
|
+
}
|
301
|
+
// 如果达到最大重试次数仍然失败,则抛出异常
|
302
|
+
throw new Error("请求失败,重试次数已达到上限!");
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* 任务
|
307
|
+
*/
|
308
|
+
const task = async (row, index) => {
|
309
|
+
try {
|
310
|
+
logger(
|
311
|
+
"**************************" + row.fileName + "**************************"
|
312
|
+
);
|
313
|
+
// Excel的列名分别为: fileName title keyword
|
314
|
+
let fileName = row.fileName;
|
315
|
+
const title = row.title;
|
316
|
+
// const keywordArr = row.keyword.split(",");
|
317
|
+
// const filteredArray = keywordArr.filter((item) => item.trim() !== "");
|
318
|
+
// const keyword = filteredArray.join(" ");
|
319
|
+
|
320
|
+
const keyword = row.keyword;
|
321
|
+
|
322
|
+
if (!fileName.includes(".")) {
|
323
|
+
fileName = row.fileName + ".mp3";
|
324
|
+
}
|
325
|
+
|
326
|
+
const getPathStartTime = new Date();
|
327
|
+
// 源音频文件夹路径
|
328
|
+
const originFilePath = await findFileInDir(
|
329
|
+
path.join(workDir, ORIGIN_FILE_DIR),
|
330
|
+
fileName
|
331
|
+
);
|
332
|
+
|
333
|
+
const getPathEndTime = new Date();
|
334
|
+
const timeInSeconds1 = ((getPathEndTime - getPathStartTime) / 1000).toFixed(
|
335
|
+
2
|
336
|
+
);
|
337
|
+
|
338
|
+
logger(`第${index}条Path路径搜索时间:${timeInSeconds1}秒`);
|
339
|
+
logger(`-------------------${new Date()}------------------`);
|
340
|
+
|
341
|
+
logger("原文件路径:" + originFilePath);
|
342
|
+
|
343
|
+
if (!originFilePath) {
|
344
|
+
logger(`音频文件 ${fileName} 不存在`);
|
345
|
+
return;
|
346
|
+
}
|
347
|
+
|
348
|
+
//index文件夹 output/0
|
349
|
+
const indexFilePath = path.join(workDir, "output", index + "");
|
350
|
+
|
351
|
+
const workStartTime = new Date();
|
352
|
+
await fs_asnyc.access(originFilePath, fs_asnyc.constants.F_OK);
|
353
|
+
logFileSize(originFilePath);
|
354
|
+
const dataParams = await getMetadata(
|
355
|
+
fileName,
|
356
|
+
indexFilePath,
|
357
|
+
originFilePath,
|
358
|
+
{
|
359
|
+
title,
|
360
|
+
keyword,
|
361
|
+
}
|
362
|
+
);
|
363
|
+
await wavAudio(indexFilePath, originFilePath, fileName);
|
364
|
+
await watermarkAudio(
|
365
|
+
indexFilePath,
|
366
|
+
originFilePath,
|
367
|
+
fileName,
|
368
|
+
dataParams.duration
|
369
|
+
);
|
370
|
+
await archiveZip(fileName, indexFilePath, originFilePath);
|
371
|
+
const workEndTime = new Date();
|
372
|
+
const timeInSeconds = ((workEndTime - workStartTime) / 1000).toFixed(2);
|
373
|
+
|
374
|
+
logger(`第${index}条4090处理总时间:${timeInSeconds}秒`);
|
375
|
+
logger(`-------------------${new Date()}------------------`);
|
376
|
+
|
377
|
+
// 重试机制
|
378
|
+
await postDataWithRetry(dataParams, indexFilePath);
|
379
|
+
|
380
|
+
logger(
|
381
|
+
`----------------------------------------第${index}条结束---------------------------------end`
|
382
|
+
);
|
383
|
+
} catch (error) {
|
384
|
+
// 可以约定code,来表示不同的错误信息
|
385
|
+
if (error.code === "ENOENT") {
|
386
|
+
logger(`音频文件 ${fileName} 不存在`);
|
387
|
+
} else {
|
388
|
+
logger("音频任务处理失败:" + error);
|
389
|
+
}
|
390
|
+
fs.rmdirSync(indexFilePath, { recursive: true });
|
391
|
+
disposeError(fileName);
|
392
|
+
|
393
|
+
//失败的时候,复制文件到错误文件夹中
|
394
|
+
const failPath = path.join(workDir, Error_Files_Dir, fileName);
|
395
|
+
moveFailFiles(originFilePath, failPath);
|
396
|
+
}
|
397
|
+
};
|
398
|
+
|
399
|
+
const main = () => {
|
400
|
+
//当前任务hash
|
401
|
+
const hashOrigin = generateUniqueHash();
|
402
|
+
const hash = hashOrigin.slice(0, 8);
|
403
|
+
logger("音频批量任务任务开始---" + hash);
|
404
|
+
logger("当前目录: " + workDir + "工作目录: " + __dirname);
|
405
|
+
|
406
|
+
const jsonData = readExcel(excelDir);
|
407
|
+
|
408
|
+
const run = (index) => {
|
409
|
+
try {
|
410
|
+
logger(
|
411
|
+
`run-------------------------------------第${index}条开始------------------------------------`
|
412
|
+
);
|
413
|
+
const row = jsonData[index];
|
414
|
+
|
415
|
+
if (!row) {
|
416
|
+
logger("最后一条已结束!");
|
417
|
+
return;
|
418
|
+
}
|
419
|
+
task(row, index)
|
420
|
+
.then(() => {
|
421
|
+
taskIndex++;
|
422
|
+
run(taskIndex);
|
423
|
+
})
|
424
|
+
.catch(() => {
|
425
|
+
taskIndex++;
|
426
|
+
run(taskIndex);
|
427
|
+
});
|
428
|
+
} catch (error) {
|
429
|
+
logger("捕获错误!" + error);
|
430
|
+
taskIndex++;
|
431
|
+
run(taskIndex);
|
432
|
+
}
|
433
|
+
};
|
434
|
+
|
435
|
+
for (let i = 0; i < queueCount; i++) {
|
436
|
+
run(i + taskIndex);
|
437
|
+
}
|
438
|
+
};
|
439
|
+
|
440
|
+
main();
|
package/utils/logger.js
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
const XLSX = require("xlsx");
|
2
|
+
const fs = require("fs");
|
3
|
+
const path = require("path");
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 普通日志
|
7
|
+
* @param {*} log 日志内容
|
8
|
+
*/
|
9
|
+
const logger = (log) => {
|
10
|
+
console.log(log);
|
11
|
+
fs.writeFileSync("log.txt", log + "\n", {
|
12
|
+
flag: "a",
|
13
|
+
});
|
14
|
+
};
|
15
|
+
|
16
|
+
//错误日志
|
17
|
+
const disposeError = (err) => {
|
18
|
+
logger("---任务失败---" + err);
|
19
|
+
logger(
|
20
|
+
`******************************************************************end`
|
21
|
+
);
|
22
|
+
fs.writeFileSync("error.txt", err + "\n", {
|
23
|
+
flag: "a",
|
24
|
+
});
|
25
|
+
};
|
26
|
+
|
27
|
+
/**
|
28
|
+
* 读取Excel
|
29
|
+
* @param {*} path Excel路径
|
30
|
+
* @returns JSON
|
31
|
+
*/
|
32
|
+
const readExcel = (path) => {
|
33
|
+
const workbook = XLSX.readFile(path);
|
34
|
+
|
35
|
+
// 获取第一个工作表(Sheet)
|
36
|
+
const firstSheetName = workbook.SheetNames[0];
|
37
|
+
const worksheet = workbook.Sheets[firstSheetName];
|
38
|
+
|
39
|
+
// 将工作表转换为 JSON 对象
|
40
|
+
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
41
|
+
return jsonData;
|
42
|
+
};
|
43
|
+
|
44
|
+
module.exports = {
|
45
|
+
readExcel,
|
46
|
+
logger,
|
47
|
+
disposeError,
|
48
|
+
};
|
@@ -0,0 +1,58 @@
|
|
1
|
+
const fs = require("fs");
|
2
|
+
const path = require("path");
|
3
|
+
const { logger } = require("../utils/logger");
|
4
|
+
|
5
|
+
function findFileInDir(dir, fileName) {
|
6
|
+
const files = fs.readdirSync(dir);
|
7
|
+
for (const file of files) {
|
8
|
+
const filePath = path.join(dir, file);
|
9
|
+
const stat = fs.statSync(filePath);
|
10
|
+
if (stat.isDirectory()) {
|
11
|
+
const result = findFileInDir(filePath, fileName);
|
12
|
+
if (result) {
|
13
|
+
return result;
|
14
|
+
}
|
15
|
+
} else if (file.startsWith(fileName)) {
|
16
|
+
return filePath;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
return null;
|
20
|
+
}
|
21
|
+
|
22
|
+
function logFileSize(path) {
|
23
|
+
const fileSize = fs.statSync(path).size / (1024 * 1024);
|
24
|
+
const formattedSize = fileSize.toFixed(2); // 保留两位小数
|
25
|
+
logger(`音频大小:${formattedSize}M`);
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* 确保目录存在,如果不存在则创建它
|
30
|
+
*/
|
31
|
+
function ensureDirSync(dirpath) {
|
32
|
+
try {
|
33
|
+
if (!fs.existsSync(dirpath)) {
|
34
|
+
fs.mkdirSync(dirpath, { recursive: true });
|
35
|
+
logger(`目录创建成功:${dirpath}`);
|
36
|
+
}
|
37
|
+
} catch (err) {
|
38
|
+
logger(`创建目录时出错:${err}`);
|
39
|
+
throw err;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
/**
|
44
|
+
* 上传失败后,存入新文件夹(手动创建)
|
45
|
+
*/
|
46
|
+
const moveFailFiles = (originFilePath, failPath) => {
|
47
|
+
if (originFilePath) {
|
48
|
+
fs.copyFileSync(originFilePath, failPath);
|
49
|
+
logger(`${originFilePath}上传失败,已移入错误文件夹!!!`);
|
50
|
+
}
|
51
|
+
};
|
52
|
+
|
53
|
+
module.exports = {
|
54
|
+
findFileInDir,
|
55
|
+
logFileSize,
|
56
|
+
ensureDirSync,
|
57
|
+
moveFailFiles,
|
58
|
+
};
|