6
6
using System . Linq ;
7
7
using System . Threading ;
8
8
using System . Threading . Tasks ;
9
+ using System . Windows . Shell ;
9
10
10
11
namespace Files . App . Helpers
11
12
{
12
- public static class ZipHelpers
13
- {
14
- public static async Task ExtractArchive ( BaseStorageFile archive , BaseStorageFolder destinationFolder , IProgress < float > progressDelegate , CancellationToken cancellationToken )
15
- {
16
- using ( SevenZipExtractor zipFile = await Filesystem . FilesystemTasks . Wrap ( async ( ) =>
17
- {
18
- var arch = new SevenZipExtractor ( await archive . OpenStreamForReadAsync ( ) ) ;
19
- return arch ? . ArchiveFileData is null ? null : arch ; // Force load archive (1665013614u)
20
- } ) )
21
- {
22
- if ( zipFile == null )
23
- {
24
- return ;
25
- }
26
- //zipFile.IsStreamOwner = true;
27
- List < ArchiveFileInfo > directoryEntries = new List < ArchiveFileInfo > ( ) ;
28
- List < ArchiveFileInfo > fileEntries = new List < ArchiveFileInfo > ( ) ;
29
- foreach ( ArchiveFileInfo entry in zipFile . ArchiveFileData )
30
- {
31
- if ( ! entry . IsDirectory )
32
- {
33
- fileEntries . Add ( entry ) ;
34
- }
35
- else
36
- {
37
- directoryEntries . Add ( entry ) ;
38
- }
39
- }
40
-
41
- if ( cancellationToken . IsCancellationRequested ) // Check if cancelled
42
- {
43
- return ;
44
- }
45
-
46
- var directories = new List < string > ( ) ;
47
- try
48
- {
49
- directories . AddRange ( directoryEntries . Select ( ( entry ) => entry . FileName ) ) ;
50
- directories . AddRange ( fileEntries . Select ( ( entry ) => Path . GetDirectoryName ( entry . FileName ) ) ) ;
51
- }
52
- catch ( Exception ex )
53
- {
54
- App . Logger . Warn ( ex , $ "Error transforming zip names into: { destinationFolder . Path } \n " +
55
- $ "Directories: { string . Join ( ", " , directoryEntries . Select ( x => x . FileName ) ) } \n " +
56
- $ "Files: { string . Join ( ", " , fileEntries . Select ( x => x . FileName ) ) } ") ;
57
- return ;
58
- }
59
-
60
- foreach ( var dir in directories . Distinct ( ) . OrderBy ( x => x . Length ) )
61
- {
62
- if ( ! NativeFileOperationsHelper . CreateDirectoryFromApp ( dir , IntPtr . Zero ) )
63
- {
64
- var dirName = destinationFolder . Path ;
65
- foreach ( var component in dir . Split ( Path . DirectorySeparatorChar , StringSplitOptions . RemoveEmptyEntries ) )
66
- {
67
- dirName = Path . Combine ( dirName , component ) ;
68
- NativeFileOperationsHelper . CreateDirectoryFromApp ( dirName , IntPtr . Zero ) ;
69
- }
70
- }
71
-
72
- if ( cancellationToken . IsCancellationRequested ) // Check if canceled
73
- {
74
- return ;
75
- }
76
- }
77
-
78
- if ( cancellationToken . IsCancellationRequested ) // Check if canceled
79
- {
80
- return ;
81
- }
82
-
83
- // Fill files
84
-
85
- byte [ ] buffer = new byte [ 4096 ] ;
86
- int entriesAmount = fileEntries . Count ;
87
- int entriesFinished = 0 ;
88
-
89
- foreach ( var entry in fileEntries )
90
- {
91
- if ( cancellationToken . IsCancellationRequested ) // Check if canceled
92
- {
93
- return ;
94
- }
95
- if ( entry . Encrypted )
96
- {
97
- App . Logger . Info ( $ "Skipped encrypted zip entry: { entry . FileName } ") ;
98
- continue ; // TODO: support password protected archives
99
- }
100
-
101
- string filePath = Path . Combine ( destinationFolder . Path , entry . FileName ) ;
102
-
103
- var hFile = NativeFileOperationsHelper . CreateFileForWrite ( filePath ) ;
104
- if ( hFile . IsInvalid )
105
- {
106
- return ; // TODO: handle error
107
- }
108
-
109
- // We don't close hFile because FileStream.Dispose() already does that
110
- using ( FileStream destinationStream = new FileStream ( hFile , FileAccess . Write ) )
111
- {
112
- try
113
- {
114
- await zipFile . ExtractFileAsync ( entry . Index , destinationStream ) ;
115
- }
116
- catch ( Exception ex )
117
- {
118
- App . Logger . Warn ( ex , $ "Error extracting file: { filePath } ") ;
119
- return ; // TODO: handle error
120
- }
121
- }
122
-
123
- entriesFinished ++ ;
124
- float percentage = ( float ) ( ( float ) entriesFinished / ( float ) entriesAmount ) * 100.0f ;
125
- progressDelegate ? . Report ( percentage ) ;
126
- }
127
- }
128
- }
129
- }
13
+ public static class ZipHelpers
14
+ {
15
+ public static async Task < bool > CompressSingleToArchive ( string sourceFolder , string archive )
16
+ {
17
+ SevenZipCompressor compressor = new ( )
18
+ {
19
+ ArchiveFormat = OutArchiveFormat . Zip ,
20
+ CompressionLevel = CompressionLevel . High ,
21
+ EventSynchronization = EventSynchronizationStrategy . AlwaysAsynchronous ,
22
+ FastCompression = true ,
23
+ IncludeEmptyDirectories = true
24
+ } ;
25
+
26
+ try
27
+ {
28
+ await compressor . CompressDirectoryAsync ( sourceFolder , archive ) ;
29
+ return true ;
30
+ }
31
+ catch ( Exception ex )
32
+ {
33
+ App . Logger . Warn ( ex , $ "Error compressing folder: { sourceFolder } ") ;
34
+ }
35
+ return false ;
36
+ }
37
+
38
+ public static async Task < bool > CompressMultipleToArchive ( string [ ] sourceFolders , string archive , IProgress < float > progressDelegate )
39
+ {
40
+ SevenZipCompressor compressor = new ( )
41
+ {
42
+ ArchiveFormat = OutArchiveFormat . Zip ,
43
+ CompressionLevel = CompressionLevel . High ,
44
+ EventSynchronization = EventSynchronizationStrategy . AlwaysAsynchronous ,
45
+ FastCompression = true ,
46
+ IncludeEmptyDirectories = true ,
47
+ PreserveDirectoryRoot = true
48
+ } ;
49
+
50
+ bool noErrors = true ;
51
+ try
52
+ {
53
+ for ( int i = 0 ; i < sourceFolders . Length ; i ++ )
54
+ {
55
+ if ( i > 0 )
56
+ compressor . CompressionMode = CompressionMode . Append ;
57
+
58
+ await compressor . CompressDirectoryAsync ( sourceFolders [ i ] , archive ) ;
59
+ float percentage = ( i + 1.0f ) / sourceFolders . Length * 100.0f ;
60
+ progressDelegate ? . Report ( percentage ) ;
61
+ }
62
+ }
63
+ catch ( Exception ex )
64
+ {
65
+ App . Logger . Warn ( ex , $ "Error compressing folder: { archive } ") ;
66
+ noErrors = false ;
67
+ }
68
+ return noErrors ;
69
+ }
70
+
71
+ public static async Task ExtractArchive ( BaseStorageFile archive , BaseStorageFolder destinationFolder , IProgress < float > progressDelegate , CancellationToken cancellationToken )
72
+ {
73
+ using ( SevenZipExtractor zipFile = await Filesystem . FilesystemTasks . Wrap ( async ( ) =>
74
+ {
75
+ var arch = new SevenZipExtractor ( await archive . OpenStreamForReadAsync ( ) ) ;
76
+ return arch ? . ArchiveFileData is null ? null : arch ; // Force load archive (1665013614u)
77
+ } ) )
78
+ {
79
+ if ( zipFile == null )
80
+ return ;
81
+ //zipFile.IsStreamOwner = true;
82
+ List < ArchiveFileInfo > directoryEntries = new List < ArchiveFileInfo > ( ) ;
83
+ List < ArchiveFileInfo > fileEntries = new List < ArchiveFileInfo > ( ) ;
84
+ foreach ( ArchiveFileInfo entry in zipFile . ArchiveFileData )
85
+ {
86
+ if ( ! entry . IsDirectory )
87
+ fileEntries . Add ( entry ) ;
88
+ else
89
+ directoryEntries . Add ( entry ) ;
90
+ }
91
+
92
+ if ( cancellationToken . IsCancellationRequested ) // Check if cancelled
93
+ return ;
94
+
95
+ var directories = new List < string > ( ) ;
96
+ try
97
+ {
98
+ directories . AddRange ( directoryEntries . Select ( ( entry ) => entry . FileName ) ) ;
99
+ directories . AddRange ( fileEntries . Select ( ( entry ) => Path . GetDirectoryName ( entry . FileName ) ) ) ;
100
+ }
101
+ catch ( Exception ex )
102
+ {
103
+ App . Logger . Warn ( ex , $ "Error transforming zip names into: { destinationFolder . Path } \n " +
104
+ $ "Directories: { string . Join ( ", " , directoryEntries . Select ( x => x . FileName ) ) } \n " +
105
+ $ "Files: { string . Join ( ", " , fileEntries . Select ( x => x . FileName ) ) } ") ;
106
+ return ;
107
+ }
108
+
109
+ foreach ( var dir in directories . Distinct ( ) . OrderBy ( x => x . Length ) )
110
+ {
111
+ if ( ! NativeFileOperationsHelper . CreateDirectoryFromApp ( dir , IntPtr . Zero ) )
112
+ {
113
+ var dirName = destinationFolder . Path ;
114
+ foreach ( var component in dir . Split ( Path . DirectorySeparatorChar , StringSplitOptions . RemoveEmptyEntries ) )
115
+ {
116
+ dirName = Path . Combine ( dirName , component ) ;
117
+ NativeFileOperationsHelper . CreateDirectoryFromApp ( dirName , IntPtr . Zero ) ;
118
+ }
119
+ }
120
+
121
+ if ( cancellationToken . IsCancellationRequested ) // Check if canceled
122
+ return ;
123
+ }
124
+
125
+ if ( cancellationToken . IsCancellationRequested ) // Check if canceled
126
+ return ;
127
+
128
+ // Fill files
129
+
130
+ byte [ ] buffer = new byte [ 4096 ] ;
131
+ int entriesAmount = fileEntries . Count ;
132
+ int entriesFinished = 0 ;
133
+
134
+ foreach ( var entry in fileEntries )
135
+ {
136
+ if ( cancellationToken . IsCancellationRequested ) // Check if canceled
137
+ return ;
138
+ if ( entry . Encrypted )
139
+ {
140
+ App . Logger . Info ( $ "Skipped encrypted zip entry: { entry . FileName } ") ;
141
+ continue ; // TODO: support password protected archives
142
+ }
143
+
144
+ string filePath = Path . Combine ( destinationFolder . Path , entry . FileName ) ;
145
+
146
+ var hFile = NativeFileOperationsHelper . CreateFileForWrite ( filePath ) ;
147
+ if ( hFile . IsInvalid )
148
+ return ; // TODO: handle error
149
+
150
+ // We don't close hFile because FileStream.Dispose() already does that
151
+ using ( FileStream destinationStream = new FileStream ( hFile , FileAccess . Write ) )
152
+ {
153
+ try
154
+ {
155
+ await zipFile . ExtractFileAsync ( entry . Index , destinationStream ) ;
156
+ }
157
+ catch ( Exception ex )
158
+ {
159
+ App . Logger . Warn ( ex , $ "Error extracting file: { filePath } ") ;
160
+ return ; // TODO: handle error
161
+ }
162
+ }
163
+
164
+ entriesFinished ++ ;
165
+ float percentage = ( float ) ( ( float ) entriesFinished / ( float ) entriesAmount ) * 100.0f ;
166
+ progressDelegate ? . Report ( percentage ) ;
167
+ }
168
+ }
169
+ }
170
+
171
+ private static async Task CopyDirectory ( BaseStorageFolder source , BaseStorageFolder destination )
172
+ {
173
+ foreach ( var file in await source . GetFilesAsync ( ) )
174
+ {
175
+ await file . CopyAsync ( destination ) ;
176
+ }
177
+ foreach ( var folder in await destination . GetFoldersAsync ( ) )
178
+ {
179
+ string subDirectoryPath = Path . Combine ( destination . Path , folder . Name ) ;
180
+ NativeFileOperationsHelper . CreateDirectoryFromApp ( subDirectoryPath , IntPtr . Zero ) ;
181
+ BaseStorageFolder subDirectory = await StorageHelpers . ToStorageItem < BaseStorageFolder > ( subDirectoryPath ) ;
182
+ await CopyDirectory ( folder , subDirectory ) ;
183
+ }
184
+ }
185
+ }
130
186
}
0 commit comments