@beltar/n8n-nodes-extract-archive 1.0.2 → 1.1.1
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.
|
@@ -40,6 +40,7 @@ const fs = __importStar(require("fs"));
|
|
|
40
40
|
const crypto = __importStar(require("crypto"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const util_1 = require("util");
|
|
43
|
+
const promises_1 = require("stream/promises");
|
|
43
44
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
44
45
|
class ExtractArchive {
|
|
45
46
|
constructor() {
|
|
@@ -143,6 +144,56 @@ class ExtractArchive {
|
|
|
143
144
|
default: 'file',
|
|
144
145
|
required: true,
|
|
145
146
|
description: 'Name of the binary property for output files. For single mode, files are named file_1, file_2, etc.',
|
|
147
|
+
displayOptions: {
|
|
148
|
+
show: {
|
|
149
|
+
saveToDisk: [false],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
displayName: 'Save to Disk',
|
|
155
|
+
name: 'saveToDisk',
|
|
156
|
+
type: 'boolean',
|
|
157
|
+
default: false,
|
|
158
|
+
description: 'Whether to save extracted files to a directory on disk instead of returning binary data. Only metadata (path, size, name) is returned.',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
displayName: 'Output Directory',
|
|
162
|
+
name: 'outputDirectory',
|
|
163
|
+
type: 'string',
|
|
164
|
+
default: '',
|
|
165
|
+
required: true,
|
|
166
|
+
placeholder: '/data/extracted',
|
|
167
|
+
description: 'Directory where extracted files will be saved',
|
|
168
|
+
displayOptions: {
|
|
169
|
+
show: {
|
|
170
|
+
saveToDisk: [true],
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
displayName: 'Create Subdirectory from Archive Name',
|
|
176
|
+
name: 'createSubdirectory',
|
|
177
|
+
type: 'boolean',
|
|
178
|
+
default: true,
|
|
179
|
+
description: 'Whether to create a subdirectory named after the archive file',
|
|
180
|
+
displayOptions: {
|
|
181
|
+
show: {
|
|
182
|
+
saveToDisk: [true],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
displayName: 'Keep Folder Structure',
|
|
188
|
+
name: 'keepFolderStructure',
|
|
189
|
+
type: 'boolean',
|
|
190
|
+
default: true,
|
|
191
|
+
description: 'Whether to preserve the folder structure from the archive. If false, all files are placed flat in the output directory.',
|
|
192
|
+
displayOptions: {
|
|
193
|
+
show: {
|
|
194
|
+
saveToDisk: [true],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
146
197
|
},
|
|
147
198
|
{
|
|
148
199
|
displayName: 'Include Subdirectories',
|
|
@@ -319,7 +370,10 @@ class ExtractArchive {
|
|
|
319
370
|
const archiveTypeParam = this.getNodeParameter('archiveType', itemIndex);
|
|
320
371
|
const password = this.getNodeParameter('password', itemIndex);
|
|
321
372
|
const outputMode = this.getNodeParameter('outputMode', itemIndex);
|
|
322
|
-
const
|
|
373
|
+
const saveToDisk = this.getNodeParameter('saveToDisk', itemIndex);
|
|
374
|
+
const outputBinaryPropertyName = !saveToDisk
|
|
375
|
+
? this.getNodeParameter('outputBinaryPropertyName', itemIndex)
|
|
376
|
+
: '';
|
|
323
377
|
const includeSubdirectories = this.getNodeParameter('includeSubdirectories', itemIndex);
|
|
324
378
|
const fileFilter = this.getNodeParameter('fileFilter', itemIndex);
|
|
325
379
|
const uniqueId = crypto.randomUUID();
|
|
@@ -374,6 +428,29 @@ class ExtractArchive {
|
|
|
374
428
|
if (files.length === 0) {
|
|
375
429
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No files were extracted. The archive might be empty or all files were filtered out.', { itemIndex });
|
|
376
430
|
}
|
|
431
|
+
// Save to disk: copy files to target directory
|
|
432
|
+
let targetDir = '';
|
|
433
|
+
if (saveToDisk) {
|
|
434
|
+
const outputDirectory = this.getNodeParameter('outputDirectory', itemIndex);
|
|
435
|
+
const createSubdirectory = this.getNodeParameter('createSubdirectory', itemIndex);
|
|
436
|
+
const keepFolderStructure = this.getNodeParameter('keepFolderStructure', itemIndex);
|
|
437
|
+
targetDir = outputDirectory;
|
|
438
|
+
if (createSubdirectory) {
|
|
439
|
+
const archiveName = path.basename(archivePath, path.extname(archivePath));
|
|
440
|
+
targetDir = path.join(outputDirectory, archiveName);
|
|
441
|
+
}
|
|
442
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
443
|
+
for (const relPath of files) {
|
|
444
|
+
const srcPath = path.join(extractDir, relPath);
|
|
445
|
+
const fileName = path.basename(relPath);
|
|
446
|
+
const destPath = keepFolderStructure
|
|
447
|
+
? path.join(targetDir, relPath)
|
|
448
|
+
: path.join(targetDir, fileName);
|
|
449
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
450
|
+
// Use streams instead of copyFileSync to avoid EPERM on network shares (CIFS/SMB)
|
|
451
|
+
await (0, promises_1.pipeline)(fs.createReadStream(srcPath), fs.createWriteStream(destPath));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
377
454
|
if (outputMode === 'single') {
|
|
378
455
|
const binary = {};
|
|
379
456
|
const fileInfos = [];
|
|
@@ -382,45 +459,74 @@ class ExtractArchive {
|
|
|
382
459
|
const fullPath = path.join(extractDir, relPath);
|
|
383
460
|
const fileBuffer = fs.readFileSync(fullPath);
|
|
384
461
|
const fileName = path.basename(relPath);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
462
|
+
if (!saveToDisk) {
|
|
463
|
+
const propertyName = files.length === 1
|
|
464
|
+
? outputBinaryPropertyName
|
|
465
|
+
: `${outputBinaryPropertyName}_${i + 1}`;
|
|
466
|
+
binary[propertyName] = await this.helpers.prepareBinaryData(fileBuffer, fileName, extractor.getMimeType(fileName));
|
|
467
|
+
}
|
|
468
|
+
const fileInfo = {
|
|
390
469
|
name: fileName,
|
|
391
470
|
path: relPath,
|
|
392
471
|
size: fileBuffer.length,
|
|
393
|
-
}
|
|
472
|
+
};
|
|
473
|
+
if (saveToDisk) {
|
|
474
|
+
const keepFolderStructure = this.getNodeParameter('keepFolderStructure', itemIndex);
|
|
475
|
+
fileInfo.diskPath = keepFolderStructure
|
|
476
|
+
? path.join(targetDir, relPath)
|
|
477
|
+
: path.join(targetDir, fileName);
|
|
478
|
+
}
|
|
479
|
+
fileInfos.push(fileInfo);
|
|
480
|
+
}
|
|
481
|
+
const json = {
|
|
482
|
+
totalFiles: files.length,
|
|
483
|
+
archiveType,
|
|
484
|
+
files: fileInfos,
|
|
485
|
+
};
|
|
486
|
+
if (!saveToDisk) {
|
|
487
|
+
json.binaryProperties = files.map((_, i) => files.length === 1 ? outputBinaryPropertyName : `${outputBinaryPropertyName}_${i + 1}`);
|
|
488
|
+
}
|
|
489
|
+
if (saveToDisk) {
|
|
490
|
+
json.outputDirectory = targetDir;
|
|
394
491
|
}
|
|
395
492
|
returnData.push({
|
|
396
|
-
json
|
|
397
|
-
|
|
398
|
-
archiveType,
|
|
399
|
-
files: fileInfos,
|
|
400
|
-
binaryProperties: files.map((_, i) => files.length === 1 ? outputBinaryPropertyName : `${outputBinaryPropertyName}_${i + 1}`),
|
|
401
|
-
},
|
|
402
|
-
binary,
|
|
493
|
+
json,
|
|
494
|
+
...(Object.keys(binary).length > 0 ? { binary } : {}),
|
|
403
495
|
});
|
|
404
496
|
}
|
|
405
497
|
else {
|
|
498
|
+
// multiple mode
|
|
406
499
|
for (let i = 0; i < files.length; i++) {
|
|
407
500
|
const relPath = files[i];
|
|
408
501
|
const fullPath = path.join(extractDir, relPath);
|
|
409
|
-
const fileBuffer = fs.readFileSync(fullPath);
|
|
410
502
|
const fileName = path.basename(relPath);
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
503
|
+
const fileSize = fs.statSync(fullPath).size;
|
|
504
|
+
const json = {
|
|
505
|
+
index: i + 1,
|
|
506
|
+
totalFiles: files.length,
|
|
507
|
+
fileName,
|
|
508
|
+
relativePath: relPath,
|
|
509
|
+
fileSize,
|
|
510
|
+
archiveType,
|
|
511
|
+
};
|
|
512
|
+
if (saveToDisk) {
|
|
513
|
+
const keepFolderStructure = this.getNodeParameter('keepFolderStructure', itemIndex);
|
|
514
|
+
json.filePath = keepFolderStructure
|
|
515
|
+
? path.join(targetDir, relPath)
|
|
516
|
+
: path.join(targetDir, fileName);
|
|
517
|
+
}
|
|
518
|
+
if (saveToDisk) {
|
|
519
|
+
returnData.push({ json });
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
const fileBuffer = fs.readFileSync(fullPath);
|
|
523
|
+
returnData.push({
|
|
524
|
+
json,
|
|
525
|
+
binary: {
|
|
526
|
+
[outputBinaryPropertyName]: await this.helpers.prepareBinaryData(fileBuffer, fileName, extractor.getMimeType(fileName)),
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
}
|
|
424
530
|
}
|
|
425
531
|
}
|
|
426
532
|
}
|