Line data Source code
1 : part of '../worker.dart';
2 :
3 : /// File compression worker (ZIP format).
4 : ///
5 : /// Compresses files or directories into ZIP archives in the background.
6 : /// Runs in native code **without** Flutter Engine for maximum efficiency.
7 : /// Perfect for log archiving, backup preparation, or reducing upload sizes.
8 : ///
9 : /// ## Basic File Compression
10 : ///
11 : /// ```dart
12 : /// await NativeWorkManager.enqueue(
13 : /// taskId: 'compress-logs',
14 : /// trigger: TaskTrigger.oneTime(),
15 : /// worker: NativeWorker.fileCompress(
16 : /// inputPath: '/app/logs/app.log',
17 : /// outputPath: '/app/archive/logs.zip',
18 : /// ),
19 : /// );
20 : /// ```
21 : ///
22 : /// ## Compress Directory with Options
23 : ///
24 : /// ```dart
25 : /// await NativeWorkManager.enqueue(
26 : /// taskId: 'compress-directory',
27 : /// trigger: TaskTrigger.oneTime(),
28 : /// worker: NativeWorker.fileCompress(
29 : /// inputPath: '/app/data/',
30 : /// outputPath: '/app/backups/data_${DateTime.now()}.zip',
31 : /// level: CompressionLevel.high,
32 : /// excludePatterns: ['*.tmp', '.DS_Store', '*.bak'],
33 : /// deleteOriginal: false,
34 : /// ),
35 : /// );
36 : /// ```
37 : ///
38 : /// ## Compress and Delete Original
39 : ///
40 : /// ```dart
41 : /// // Archive old logs and delete originals to save space
42 : /// await NativeWorkManager.enqueue(
43 : /// taskId: 'archive-old-logs',
44 : /// trigger: TaskTrigger.oneTime(),
45 : /// worker: NativeWorker.fileCompress(
46 : /// inputPath: '/app/logs/2025/',
47 : /// outputPath: '/app/archive/logs_2025.zip',
48 : /// level: CompressionLevel.medium,
49 : /// deleteOriginal: true, // Delete source after compression
50 : /// ),
51 : /// constraints: Constraints(requiresStorageNotLow: true),
52 : /// );
53 : /// ```
54 : ///
55 : /// ## Periodic Log Archiving
56 : ///
57 : /// ```dart
58 : /// // Compress logs daily
59 : /// await NativeWorkManager.enqueue(
60 : /// taskId: 'daily-log-archive',
61 : /// trigger: TaskTrigger.periodic(Duration(days: 1)),
62 : /// worker: NativeWorker.fileCompress(
63 : /// inputPath: '/app/logs/',
64 : /// outputPath: '/app/archive/logs_\${DateTime.now().day}.zip',
65 : /// excludePatterns: ['current.log'], // Keep current log
66 : /// ),
67 : /// );
68 : /// ```
69 : ///
70 : /// ## Parameters
71 : ///
72 : /// **[inputPath]** *(required)* - Path to file or directory to compress.
73 : /// - Must be absolute path
74 : /// - Can be a single file or directory
75 : /// - Directory will be compressed recursively
76 : /// - Throws `ArgumentError` if empty or doesn't exist
77 : ///
78 : /// **[outputPath]** *(required)* - Where to save the ZIP file.
79 : /// - Must be absolute path ending with .zip
80 : /// - Parent directory will be created if needed
81 : /// - Existing file will be overwritten
82 : /// - Throws `ArgumentError` if empty or doesn't end with .zip
83 : ///
84 : /// **[level]** *(optional)* - Compression level (default: medium).
85 : /// - `CompressionLevel.low` - Faster compression, larger file
86 : /// - `CompressionLevel.medium` - Balanced (recommended)
87 : /// - `CompressionLevel.high` - Best compression, slower
88 : ///
89 : /// **[excludePatterns]** *(optional)* - Patterns to exclude (default: empty).
90 : /// - Supports wildcards: `*.tmp`, `temp*`, `*backup*`
91 : /// - Exact match: `.DS_Store`, `Thumbs.db`
92 : /// - Case-insensitive matching
93 : ///
94 : /// **[deleteOriginal]** *(optional)* - Delete source after compression (default: false).
95 : /// - Use with caution!
96 : /// - Only deletes if compression succeeds
97 : /// - Cannot be undone
98 : ///
99 : /// ## Progress Tracking
100 : ///
101 : /// ```dart
102 : /// // Listen to compression progress
103 : /// NativeWorkManager.progress
104 : /// .where((p) => p.taskId == 'my-compression')
105 : /// .listen((progress) {
106 : /// print('Compressed: ${progress.currentStep}/${progress.totalSteps} files');
107 : /// print('Progress: ${progress.progress}%');
108 : /// });
109 : /// ```
110 : ///
111 : /// ## Behavior
112 : ///
113 : /// - Compresses using ZIP format (universal compatibility)
114 : /// - Preserves file modification times
115 : /// - Creates parent directories automatically
116 : /// - Overwrites existing output file
117 : /// - Reports progress via [NativeWorkManager.progress] stream
118 : /// - Task succeeds if compression completes successfully
119 : /// - Task fails on I/O error, missing file, or insufficient storage
120 : ///
121 : /// ## When to Use
122 : ///
123 : /// ✅ **Use fileCompress when:**
124 : /// - Archiving log files periodically
125 : /// - Preparing backups for upload
126 : /// - Reducing file sizes before transfer
127 : /// - Freeing up storage space
128 : /// - Creating distributable packages
129 : ///
130 : /// ❌ **Don't use fileCompress when:**
131 : /// - Files are already compressed (JPEG, PNG, MP4, PDF)
132 : /// - Need other formats (7z, RAR, tar.gz) → Use custom worker
133 : /// - Need encryption → Use FileEncryptionWorker (v1.1+)
134 : ///
135 : /// ## Common Pitfalls
136 : ///
137 : /// ❌ **Don't** compress already-compressed files (no benefit)
138 : /// ❌ **Don't** use deleteOriginal without backup
139 : /// ❌ **Don't** forget storage constraints for large files
140 : /// ❌ **Don't** compress system directories
141 : /// ✅ **Do** use excludePatterns to skip unnecessary files
142 : /// ✅ **Do** check available storage before large compressions
143 : /// ✅ **Do** use periodic tasks for automated archiving
144 : /// ✅ **Do** test with small files first
145 : ///
146 : /// ## Platform Notes
147 : ///
148 : /// **Android:**
149 : /// - Uses `java.util.zip.ZipOutputStream`
150 : /// - Supports all compression levels
151 : /// - No file size limit (system dependent)
152 : ///
153 : /// **iOS:**
154 : /// - Uses `Compression` framework (iOS 13+)
155 : /// - Supports all compression levels
156 : /// - No file size limit (system dependent)
157 : ///
158 : /// ## Performance
159 : ///
160 : /// | File Size | Low | Medium | High | Note |
161 : /// |-----------|-----|--------|------|------|
162 : /// | 10 MB | ~1s | ~2s | ~3s | Text files |
163 : /// | 100 MB | ~8s | ~15s | ~25s | Mixed content |
164 : /// | 1 GB | ~80s | ~150s | ~250s | Use constraints! |
165 : ///
166 : /// **Tip:** For large files (>100MB), use:
167 : /// ```dart
168 : /// constraints: Constraints(
169 : /// requiresCharging: true,
170 : /// requiresDeviceIdle: true,
171 : /// requiresStorageNotLow: true,
172 : /// )
173 : /// ```
174 : ///
175 : /// ## See Also
176 : ///
177 : /// - [NativeWorker.httpUpload] - Upload compressed files
178 : /// - [NativeWorkManager.progress] - Track compression progress
179 : /// - [NativeWorker.custom] - Custom compression formats
180 4 : Worker _buildFileCompress({
181 : required String inputPath,
182 : required String outputPath,
183 : CompressionLevel level = CompressionLevel.medium,
184 : List<String> excludePatterns = const [],
185 : bool deleteOriginal = false,
186 : }) {
187 4 : NativeWorker._validateFilePath(inputPath, 'inputPath');
188 4 : NativeWorker._validateFilePath(outputPath, 'outputPath');
189 :
190 8 : if (!outputPath.toLowerCase().endsWith('.zip')) {
191 4 : throw ArgumentError(
192 : 'Output path must end with .zip\n'
193 : 'Current: $outputPath\n'
194 : 'Example: /app/archive/backup.zip',
195 : );
196 : }
197 :
198 4 : return FileCompressionWorker(
199 : inputPath: inputPath,
200 : outputPath: outputPath,
201 : level: level,
202 : excludePatterns: excludePatterns,
203 : deleteOriginal: deleteOriginal,
204 : );
205 : }
206 :
207 : /// File decompression worker (ZIP extraction).
208 : ///
209 : /// Extracts files from ZIP archives in the background. Supports password-protected
210 : /// archives, selective extraction, and zip bomb protection. Runs in native code
211 : /// **without** Flutter Engine for maximum efficiency.
212 : ///
213 : /// ## Basic Extraction
214 : ///
215 : /// ```dart
216 : /// await NativeWorkManager.enqueue(
217 : /// taskId: 'extract-backup',
218 : /// trigger: TaskTrigger.oneTime(),
219 : /// worker: NativeWorker.fileDecompress(
220 : /// archivePath: '/app/downloads/backup.zip',
221 : /// destinationPath: '/app/data/restored/',
222 : /// ),
223 : /// );
224 : /// ```
225 : ///
226 : /// ## Extract Specific Files Only
227 : ///
228 : /// ```dart
229 : /// await NativeWorkManager.enqueue(
230 : /// taskId: 'extract-config',
231 : /// trigger: TaskTrigger.oneTime(),
232 : /// worker: NativeWorker.fileDecompress(
233 : /// archivePath: '/app/downloads/package.zip',
234 : /// destinationPath: '/app/config/',
235 : /// extractFiles: ['config.json', 'settings.xml'],
236 : /// ),
237 : /// );
238 : /// ```
239 : ///
240 : /// ## Password-Protected Archive
241 : ///
242 : /// ```dart
243 : /// await NativeWorkManager.enqueue(
244 : /// taskId: 'extract-secure',
245 : /// trigger: TaskTrigger.oneTime(),
246 : /// worker: NativeWorker.fileDecompress(
247 : /// archivePath: '/app/downloads/secure.zip',
248 : /// destinationPath: '/app/private/',
249 : /// password: 'mySecurePassword',
250 : /// ),
251 : /// );
252 : /// ```
253 : ///
254 : /// ## Extract and Delete Archive
255 : ///
256 : /// ```dart
257 : /// await NativeWorkManager.enqueue(
258 : /// taskId: 'extract-temp',
259 : /// trigger: TaskTrigger.oneTime(),
260 : /// worker: NativeWorker.fileDecompress(
261 : /// zipPath: '/app/downloads/data.zip',
262 : /// targetDir: '/app/temp/',
263 : /// deleteAfterExtract: true, // Save storage space
264 : /// overwrite: true,
265 : /// ),
266 : /// );
267 : /// ```
268 : ///
269 : /// ## Complete Workflow: Download → Extract → Process
270 : ///
271 : /// ```dart
272 : /// // Step 1: Download ZIP
273 : /// await NativeWorkManager.enqueue(
274 : /// taskId: 'download-data',
275 : /// trigger: TaskTrigger.oneTime(),
276 : /// worker: NativeWorker.httpDownload(
277 : /// url: 'https://cdn.example.com/data.zip',
278 : /// savePath: '/app/downloads/data.zip',
279 : /// ),
280 : /// );
281 : ///
282 : /// // Step 2: Extract downloaded ZIP
283 : /// await NativeWorkManager.enqueue(
284 : /// taskId: 'extract-data',
285 : /// trigger: TaskTrigger.contentUri(taskId: 'download-data'),
286 : /// worker: NativeWorker.fileDecompress(
287 : /// zipPath: '/app/downloads/data.zip',
288 : /// targetDir: '/app/data/',
289 : /// deleteAfterExtract: true,
290 : /// ),
291 : /// );
292 : /// ```
293 : ///
294 : /// ## Parameters
295 : ///
296 : /// **[zipPath]** *(required)* - Path to ZIP archive to extract.
297 : /// - Must be absolute path
298 : /// - File must exist at execution time
299 : /// - Throws `ArgumentError` if empty
300 : ///
301 : /// **[targetDir]** *(required)* - Directory where files will be extracted.
302 : /// - Must be absolute path
303 : /// - Directory will be created if it doesn't exist
304 : /// - Throws `ArgumentError` if empty
305 : ///
306 : /// **[deleteAfterExtract]** *(optional)* - Delete archive after extraction (default: false).
307 : /// - Saves storage space
308 : /// - Only deletes if extraction succeeds
309 : /// - Use with caution!
310 : ///
311 : /// **[overwrite]** *(optional)* - Overwrite existing files (default: true).
312 : /// - If false, skips files that already exist
313 : /// - If true, replaces existing files
314 : ///
315 : /// ## Progress Tracking
316 : ///
317 : /// ```dart
318 : /// // Listen to extraction progress
319 : /// NativeWorkManager.progress
320 : /// .where((p) => p.taskId == 'my-extraction')
321 : /// .listen((progress) {
322 : /// print('Extracted: ${progress.currentStep}/${progress.totalSteps} files');
323 : /// print('Progress: ${progress.progress}%');
324 : /// });
325 : /// ```
326 : ///
327 : /// ## Behavior
328 : ///
329 : /// - Extracts all files preserving directory structure
330 : /// - Creates destination directory if needed
331 : /// - Path traversal protection (prevents ../../../ attacks)
332 : /// - Validates uncompressed size before extraction
333 : /// - Reports progress via [NativeWorkManager.progress] stream
334 : /// - Task succeeds if extraction completes successfully
335 : /// - Task fails on I/O error, wrong password, or zip bomb detected
336 : ///
337 : /// ## When to Use
338 : ///
339 : /// ✅ **Use fileDecompress when:**
340 : /// - Extracting downloaded content packages
341 : /// - Restoring backups
342 : /// - Unpacking app resources
343 : /// - Processing uploaded archives
344 : /// - Handling OTA update packages
345 : ///
346 : /// ❌ **Don't use fileDecompress when:**
347 : /// - Archive format is not ZIP → Use custom worker
348 : /// - Need to extract on-the-fly during download → Use streaming
349 : /// - Archive is untrusted → Validate maxSizeBytes carefully
350 : ///
351 : /// ## Security Notes
352 : ///
353 : /// **Zip Bomb Protection:**
354 : /// - Built-in validation prevents decompression bombs
355 : /// - Checks extracted size during extraction
356 : /// - Task fails if suspicious expansion detected
357 : ///
358 : /// **Path Traversal Protection:**
359 : /// - Automatically validates all file paths
360 : /// - Prevents extraction outside destination directory
361 : /// - Blocks malicious paths like `../../etc/passwd`
362 : ///
363 : /// ## Common Pitfalls
364 : ///
365 : /// ❌ **Don't** extract untrusted archives without validation
366 : /// ❌ **Don't** use deleteAfterExtract without verifying extraction success
367 : /// ❌ **Don't** forget to handle task failure (archive may be corrupt)
368 : /// ❌ **Don't** extract to system directories
369 : /// ✅ **Do** use storage constraints for large archives
370 : /// ✅ **Do** validate archive integrity before extraction
371 : /// ✅ **Do** test with known-good archives first
372 : ///
373 : /// ## Platform Notes
374 : ///
375 : /// **Android:**
376 : /// - Uses standard Java ZIP libraries
377 : /// - Supports all ZIP formats including ZIP64
378 : /// - Streaming extraction (low memory)
379 : ///
380 : /// **iOS:**
381 : /// - Uses ZIPFoundation framework
382 : /// - Streaming extraction (low memory)
383 : /// - Built-in security validations
384 : ///
385 : /// ## Future Features (v1.1.0)
386 : ///
387 : /// - Password-protected ZIP support
388 : /// - Selective file extraction
389 : /// - Custom extraction filters
390 : ///
391 : /// ## Performance
392 : ///
393 : /// | Archive Size | Files | Time | Note |
394 : /// |--------------|-------|------|------|
395 : /// | 10 MB | 100 | ~1s | Small packages |
396 : /// | 100 MB | 1000 | ~8s | Medium backups |
397 : /// | 500 MB | 5000 | ~40s | Large archives |
398 : ///
399 : /// **Tip:** For large archives (>100MB), use:
400 : /// ```dart
401 : /// constraints: Constraints(
402 : /// requiresStorageNotLow: true,
403 : /// )
404 : /// ```
405 : ///
406 : /// ## See Also
407 : ///
408 : /// - `NativeWorker.fileCompress` (deprecated v1.1.0 — use `archive` package)
409 : /// - [NativeWorker.httpDownload] - Download ZIP archives
410 : /// - [NativeWorkManager.progress] - Track extraction progress
411 4 : Worker _buildFileDecompress({
412 : required String zipPath,
413 : required String targetDir,
414 : bool deleteAfterExtract = false,
415 : bool overwrite = true,
416 : }) {
417 4 : NativeWorker._validateFilePath(zipPath, 'zipPath');
418 4 : NativeWorker._validateFilePath(targetDir, 'targetDir');
419 :
420 8 : if (!zipPath.toLowerCase().endsWith('.zip')) {
421 4 : throw ArgumentError(
422 : 'ZIP path must end with .zip\n'
423 : 'Current: $zipPath\n'
424 : 'Example: /app/downloads/archive.zip',
425 : );
426 : }
427 :
428 3 : return FileDecompressionWorker(
429 : zipPath: zipPath,
430 : targetDir: targetDir,
431 : deleteAfterExtract: deleteAfterExtract,
432 : overwrite: overwrite,
433 : );
434 : }
435 :
436 : /// Copy file or directory worker.
437 : ///
438 : /// Copies files or directories for pure-native task chains **without** Flutter Engine.
439 : /// Useful for organizing files, creating backups, or duplicating data.
440 : ///
441 : /// ## Basic File Copy
442 : ///
443 : /// ```dart
444 : /// await NativeWorkManager.enqueue(
445 : /// taskId: 'copy-file',
446 : /// trigger: TaskTrigger.oneTime(),
447 : /// worker: NativeWorker.fileCopy(
448 : /// sourcePath: '/downloads/photo.jpg',
449 : /// destinationPath: '/backups/photo.jpg',
450 : /// ),
451 : /// );
452 : /// ```
453 : ///
454 : /// ## Copy Directory
455 : ///
456 : /// ```dart
457 : /// await NativeWorkManager.enqueue(
458 : /// taskId: 'copy-directory',
459 : /// trigger: TaskTrigger.oneTime(),
460 : /// worker: NativeWorker.fileCopy(
461 : /// sourcePath: '/photos/vacation',
462 : /// destinationPath: '/backups/vacation',
463 : /// recursive: true,
464 : /// ),
465 : /// );
466 : /// ```
467 : ///
468 : /// ## Parameters
469 : ///
470 : /// **[sourcePath]** *(required)* - Path to source file or directory.
471 : ///
472 : /// **[destinationPath]** *(required)* - Where to copy the file/directory.
473 : ///
474 : /// **[overwrite]** *(optional)* - Overwrite if destination exists (default: false).
475 : ///
476 : /// **[recursive]** *(optional)* - Copy directories recursively (default: true).
477 : ///
478 : /// ## See Also
479 : ///
480 : /// - [NativeWorker.fileMove] - Move files instead of copying
481 : /// - [NativeWorker.fileDelete] - Delete files
482 3 : Worker _buildFileCopy({
483 : required String sourcePath,
484 : required String destinationPath,
485 : bool overwrite = false,
486 : bool recursive = true,
487 : }) {
488 3 : NativeWorker._validateFilePath(sourcePath, 'sourcePath');
489 3 : NativeWorker._validateFilePath(destinationPath, 'destinationPath');
490 :
491 3 : return FileSystemCopyWorker(
492 : sourcePath: sourcePath,
493 : destinationPath: destinationPath,
494 : overwrite: overwrite,
495 : recursive: recursive,
496 : );
497 : }
498 :
499 : /// Move file or directory worker.
500 : ///
501 : /// Moves files or directories for pure-native task chains **without** Flutter Engine.
502 : /// More efficient than copy+delete for large files (atomic operation when possible).
503 : ///
504 : /// ## Basic File Move
505 : ///
506 : /// ```dart
507 : /// await NativeWorkManager.enqueue(
508 : /// taskId: 'move-file',
509 : /// trigger: TaskTrigger.oneTime(),
510 : /// worker: NativeWorker.fileMove(
511 : /// sourcePath: '/temp/download.zip',
512 : /// destinationPath: '/downloads/file.zip',
513 : /// ),
514 : /// );
515 : /// ```
516 : ///
517 : /// ## Parameters
518 : ///
519 : /// **[sourcePath]** *(required)* - Path to source file or directory.
520 : ///
521 : /// **[destinationPath]** *(required)* - Where to move the file/directory.
522 : ///
523 : /// **[overwrite]** *(optional)* - Overwrite if destination exists (default: false).
524 : ///
525 : /// ## See Also
526 : ///
527 : /// - [NativeWorker.fileCopy] - Copy files instead of moving
528 : /// - [NativeWorker.fileDelete] - Delete files after processing
529 3 : Worker _buildFileMove({
530 : required String sourcePath,
531 : required String destinationPath,
532 : bool overwrite = false,
533 : }) {
534 3 : NativeWorker._validateFilePath(sourcePath, 'sourcePath');
535 3 : NativeWorker._validateFilePath(destinationPath, 'destinationPath');
536 :
537 3 : return FileSystemMoveWorker(
538 : sourcePath: sourcePath,
539 : destinationPath: destinationPath,
540 : overwrite: overwrite,
541 : );
542 : }
543 :
544 : /// Delete file or directory worker.
545 : ///
546 : /// Deletes files or directories for cleanup in pure-native task chains **without** Flutter Engine.
547 : ///
548 : /// ## Basic File Delete
549 : ///
550 : /// ```dart
551 : /// await NativeWorkManager.enqueue(
552 : /// taskId: 'cleanup',
553 : /// trigger: TaskTrigger.oneTime(),
554 : /// worker: NativeWorker.fileDelete(
555 : /// path: '/temp/cache.dat',
556 : /// ),
557 : /// );
558 : /// ```
559 : ///
560 : /// ## Delete Directory
561 : ///
562 : /// ```dart
563 : /// await NativeWorkManager.enqueue(
564 : /// taskId: 'cleanup-temp',
565 : /// trigger: TaskTrigger.oneTime(),
566 : /// worker: NativeWorker.fileDelete(
567 : /// path: '/temp',
568 : /// recursive: true, // Delete all contents
569 : /// ),
570 : /// );
571 : /// ```
572 : ///
573 : /// ## Parameters
574 : ///
575 : /// **[path]** *(required)* - Path to file or directory to delete.
576 : ///
577 : /// **[recursive]** *(optional)* - Delete directories recursively (default: false).
578 : /// - If false and path is directory, task fails
579 : /// - If true, deletes directory and all contents
580 : ///
581 : /// ## Safety
582 : ///
583 : /// - Protected paths (/, /system, etc.) cannot be deleted
584 : /// - Deletion is permanent (no trash/recycle bin)
585 : ///
586 : /// ## See Also
587 : ///
588 : /// - [NativeWorker.fileCopy] - Copy files before deleting
589 : /// - [NativeWorker.fileMove] - Move files instead of deleting
590 4 : Worker _buildFileDelete({required String path, bool recursive = false}) {
591 4 : NativeWorker._validateFilePath(path, 'path');
592 :
593 3 : return FileSystemDeleteWorker(path: path, recursive: recursive);
594 : }
595 :
596 : /// List directory contents worker.
597 : ///
598 : /// Lists files in a directory for pure-native task chains **without** Flutter Engine.
599 : /// Useful for scanning directories, finding files, or building file indexes.
600 : ///
601 : /// ## Basic Directory Listing
602 : ///
603 : /// ```dart
604 : /// await NativeWorkManager.enqueue(
605 : /// taskId: 'list-files',
606 : /// trigger: TaskTrigger.oneTime(),
607 : /// worker: NativeWorker.fileList(
608 : /// path: '/downloads',
609 : /// ),
610 : /// );
611 : /// ```
612 : ///
613 : /// ## List with Pattern
614 : ///
615 : /// ```dart
616 : /// // Find all JPG files
617 : /// await NativeWorkManager.enqueue(
618 : /// taskId: 'find-photos',
619 : /// trigger: TaskTrigger.oneTime(),
620 : /// worker: NativeWorker.fileList(
621 : /// path: '/photos',
622 : /// pattern: '*.jpg',
623 : /// recursive: true,
624 : /// ),
625 : /// );
626 : /// ```
627 : ///
628 : /// ## Parameters
629 : ///
630 : /// **[path]** *(required)* - Directory path to list.
631 : ///
632 : /// **[pattern]** *(optional)* - Glob pattern to filter files (e.g., "*.jpg", "file_*.txt").
633 : /// - Supports wildcards: `*` (any chars), `?` (single char)
634 : /// - Example: `*.jpg` matches all JPEG files
635 : /// - Example: `photo_?.png` matches `photo_1.png`, `photo_a.png`
636 : ///
637 : /// **[recursive]** *(optional)* - List subdirectories recursively (default: false).
638 : ///
639 : /// ## Result
640 : ///
641 : /// Returns list of file info with:
642 : /// - `path` - Full file path
643 : /// - `name` - File name
644 : /// - `size` - File size in bytes
645 : /// - `lastModified` - Last modification timestamp
646 : ///
647 : /// ## See Also
648 : ///
649 : /// - [NativeWorker.fileDelete] - Delete found files
650 : /// - [NativeWorker.fileCopy] - Copy found files
651 2 : Worker _buildFileList({
652 : required String path,
653 : String? pattern,
654 : bool recursive = false,
655 : }) {
656 2 : NativeWorker._validateFilePath(path, 'path');
657 :
658 2 : return FileSystemListWorker(
659 : path: path,
660 : pattern: pattern,
661 : recursive: recursive,
662 : );
663 : }
664 :
665 : /// Create directory worker (mkdir).
666 : ///
667 : /// Creates directories for pure-native task chains **without** Flutter Engine.
668 : /// Useful for setting up folder structure before file operations.
669 : ///
670 : /// ## Create Directory
671 : ///
672 : /// ```dart
673 : /// await NativeWorkManager.enqueue(
674 : /// taskId: 'create-backup-dir',
675 : /// trigger: TaskTrigger.oneTime(),
676 : /// worker: NativeWorker.fileMkdir(
677 : /// path: '/backups/2024-02-07',
678 : /// ),
679 : /// );
680 : /// ```
681 : ///
682 : /// ## Parameters
683 : ///
684 : /// **[path]** *(required)* - Directory path to create.
685 : ///
686 : /// **[createParents]** *(optional)* - Create parent directories if needed (default: true).
687 : /// - If true, creates `/backups/2024/02/07` even if `/backups` doesn't exist
688 : /// - If false, fails if parent doesn't exist
689 : ///
690 : /// ## See Also
691 : ///
692 : /// - [NativeWorker.fileCopy] - Copy files after creating directory
693 : /// - [NativeWorker.fileMove] - Move files to new directory
694 2 : Worker _buildFileMkdir({required String path, bool createParents = true}) {
695 2 : NativeWorker._validateFilePath(path, 'path');
696 :
697 2 : return FileSystemMkdirWorker(path: path, createParents: createParents);
698 : }
|