omnizip 0.3.35 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +67 -0
- data/docs/getting-started/basic-usage.adoc +14 -22
- data/docs/guides/advanced-features/encryption.adoc +16 -39
- data/docs/guides/advanced-features/index.adoc +16 -6
- data/docs/guides/advanced-features/parallel-processing.adoc +63 -165
- data/docs/guides/advanced-features/progress-tracking.adoc +65 -261
- data/docs/guides/advanced-features/streaming.adoc +43 -113
- data/docs/guides/compression-algorithms/bzip2.adoc +2 -1
- data/docs/guides/compression-algorithms/deflate.adoc +1 -7
- data/docs/guides/compression-algorithms/lzma.adoc +10 -14
- data/docs/guides/compression-algorithms/lzma2.adoc +34 -117
- data/docs/guides/compression-algorithms/ppmd.adoc +24 -27
- data/docs/guides/compression-algorithms/zstandard.adoc +85 -150
- data/docs/troubleshooting/index.adoc +7 -5
- data/lib/omnizip/algorithms/zstandard.rb +2 -3
- data/lib/omnizip/archive/builder.rb +48 -0
- data/lib/omnizip/archive/reader_session.rb +67 -0
- data/lib/omnizip/archive.rb +86 -0
- data/lib/omnizip/archive_handler.rb +1 -0
- data/lib/omnizip/archive_handlers/rar_handler.rb +50 -0
- data/lib/omnizip/archive_handlers/seven_zip_handler.rb +6 -2
- data/lib/omnizip/archive_handlers/tar_handler.rb +4 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +25 -2
- data/lib/omnizip/archive_handlers.rb +1 -0
- data/lib/omnizip/convenience.rb +91 -11
- data/lib/omnizip/formats/rar/block_parser.rb +8 -12
- data/lib/omnizip/formats/rar/constants.rb +3 -1
- data/lib/omnizip/formats/rar/header.rb +16 -6
- data/lib/omnizip/formats/rar/reader.rb +6 -2
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip.rb +1 -0
- metadata +6 -2
|
@@ -7,314 +7,118 @@ grand_parent: Guides
|
|
|
7
7
|
|
|
8
8
|
== Purpose
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Long compression and extraction jobs benefit from progress feedback.
|
|
11
|
+
Omnizip ships a standalone tracker — +Omnizip::Progress+ — with
|
|
12
|
+
percentage, throughput, and ETA calculation. The convenience methods
|
|
13
|
+
do not emit progress themselves: wrap your own loop with a tracker.
|
|
11
14
|
|
|
12
|
-
==
|
|
15
|
+
== Basic Tracking
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
* **Better UX**: Prevent "frozen" appearance during processing
|
|
16
|
-
* **Performance Monitoring**: Track processing rates and bottlenecks
|
|
17
|
-
* **Time Estimation**: Know how long operations will take
|
|
18
|
-
* **Debugging**: Identify which files cause slowdowns
|
|
19
|
-
|
|
20
|
-
== Progress Callbacks
|
|
21
|
-
|
|
22
|
-
=== Basic Progress Monitoring
|
|
23
|
-
|
|
24
|
-
Get updates during compression or extraction:
|
|
17
|
+
Create a tracker, update it as work completes, read live stats:
|
|
25
18
|
|
|
26
19
|
[source,ruby]
|
|
27
20
|
----
|
|
28
|
-
Omnizip.
|
|
29
|
-
puts "Progress: #{progress.percentage}%"
|
|
30
|
-
puts "Current file: #{progress.current_file}"
|
|
31
|
-
puts "Files: #{progress.files_processed}/#{progress.total_files}"
|
|
32
|
-
end
|
|
33
|
-
----
|
|
34
|
-
|
|
35
|
-
=== Detailed Progress Information
|
|
21
|
+
tracker = Omnizip::Progress.track(total_files: 100, total_bytes: 1_000_000)
|
|
36
22
|
|
|
37
|
-
|
|
23
|
+
tracker.update(files: 10, bytes: 100_000, current_file: "file.txt")
|
|
38
24
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
puts "Bytes: #{progress.bytes_processed}/#{progress.total_bytes}"
|
|
44
|
-
puts "Speed: #{progress.throughput_mb_s} MB/s"
|
|
45
|
-
puts "Elapsed: #{progress.elapsed_seconds}s"
|
|
46
|
-
puts "ETA: #{progress.eta_seconds}s"
|
|
47
|
-
puts "Percentage: #{progress.percentage}%"
|
|
48
|
-
end
|
|
25
|
+
tracker.percentage # => 10.0
|
|
26
|
+
tracker.elapsed_seconds
|
|
27
|
+
tracker.eta_seconds
|
|
28
|
+
tracker.eta_formatted # => "2m 30s"
|
|
49
29
|
----
|
|
50
30
|
|
|
51
|
-
==
|
|
31
|
+
== Reporters
|
|
52
32
|
|
|
53
|
-
===
|
|
33
|
+
=== Callback Reporter
|
|
54
34
|
|
|
55
|
-
|
|
35
|
+
Yield progress to a block:
|
|
56
36
|
|
|
57
37
|
[source,ruby]
|
|
58
38
|
----
|
|
59
|
-
Omnizip.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
progress: :console # Show progress bar
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
# Output:
|
|
66
|
-
# [=========> ] 45% (23/50 files) - 2.5 MB/s - ETA: 12s
|
|
67
|
-
----
|
|
68
|
-
|
|
69
|
-
=== Custom Progress Bar
|
|
70
|
-
|
|
71
|
-
Configure progress bar appearance:
|
|
72
|
-
|
|
73
|
-
[source,ruby]
|
|
74
|
-
----
|
|
75
|
-
options = {
|
|
76
|
-
progress: :console,
|
|
77
|
-
progress_format: :detailed, # or :simple, :verbose
|
|
78
|
-
update_interval: 0.5, # Update every 500ms
|
|
79
|
-
show_rate: true,
|
|
80
|
-
show_eta: true
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
Omnizip.compress_directory('files/', 'backup.7z', **options)
|
|
84
|
-
----
|
|
85
|
-
|
|
86
|
-
== Web Application Integration
|
|
87
|
-
|
|
88
|
-
=== Rails Real-Time Updates
|
|
89
|
-
|
|
90
|
-
Send progress updates to the browser via WebSocket:
|
|
91
|
-
|
|
92
|
-
[source,ruby]
|
|
93
|
-
----
|
|
94
|
-
class ArchiveJob < ApplicationJob
|
|
95
|
-
def perform(user_id, file_paths)
|
|
96
|
-
ActionCable.server.broadcast("archive_#{user_id}", {
|
|
97
|
-
status: 'starting',
|
|
98
|
-
total_files: file_paths.count
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
Omnizip.compress_files(file_paths, 'output.zip') do |progress|
|
|
102
|
-
ActionCable.server.broadcast("archive_#{user_id}", {
|
|
103
|
-
status: 'processing',
|
|
104
|
-
percentage: progress.percentage,
|
|
105
|
-
files_done: progress.files_processed,
|
|
106
|
-
current_file: progress.current_file,
|
|
107
|
-
eta: progress.eta_seconds
|
|
108
|
-
})
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
ActionCable.server.broadcast("archive_#{user_id}", {
|
|
112
|
-
status: 'complete',
|
|
113
|
-
download_url: archive_url
|
|
114
|
-
})
|
|
115
|
-
end
|
|
39
|
+
tracker = Omnizip::Progress.track(total_files: 100,
|
|
40
|
+
total_bytes: 1_000_000) do |progress|
|
|
41
|
+
puts "#{progress.percentage}% complete"
|
|
116
42
|
end
|
|
117
43
|
----
|
|
118
44
|
|
|
119
|
-
===
|
|
45
|
+
=== Console Reporter
|
|
120
46
|
|
|
121
|
-
|
|
47
|
+
Render a terminal progress bar:
|
|
122
48
|
|
|
123
49
|
[source,ruby]
|
|
124
50
|
----
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Omnizip.compress_directory('data/', 'output.zip') do |progress|
|
|
131
|
-
# Store progress in Redis/cache
|
|
132
|
-
Rails.cache.write("archive_progress_#{job_id}", {
|
|
133
|
-
percentage: progress.percentage,
|
|
134
|
-
files: progress.files_processed,
|
|
135
|
-
total: progress.total_files,
|
|
136
|
-
eta: progress.eta_seconds
|
|
137
|
-
})
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
render json: { job_id: job_id }
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Check progress
|
|
145
|
-
def check_progress
|
|
146
|
-
progress = Rails.cache.read("archive_progress_#{params[:job_id]}")
|
|
147
|
-
render json: progress
|
|
148
|
-
end
|
|
149
|
-
----
|
|
150
|
-
|
|
151
|
-
== Progress Metrics
|
|
152
|
-
|
|
153
|
-
=== Available Metrics
|
|
154
|
-
|
|
155
|
-
[cols="2,3"]
|
|
156
|
-
|===
|
|
157
|
-
|Metric |Description
|
|
158
|
-
|
|
159
|
-
|`percentage`
|
|
160
|
-
|Overall completion percentage (0-100)
|
|
161
|
-
|
|
162
|
-
|`files_processed`
|
|
163
|
-
|Number of files processed so far
|
|
164
|
-
|
|
165
|
-
|`total_files`
|
|
166
|
-
|Total number of files to process
|
|
167
|
-
|
|
168
|
-
|`bytes_processed`
|
|
169
|
-
|Bytes processed so far
|
|
170
|
-
|
|
171
|
-
|`total_bytes`
|
|
172
|
-
|Total bytes to process
|
|
173
|
-
|
|
174
|
-
|`current_file`
|
|
175
|
-
|Name of file currently being processed
|
|
176
|
-
|
|
177
|
-
|`throughput_mb_s`
|
|
178
|
-
|Processing speed in MB/second
|
|
179
|
-
|
|
180
|
-
|`elapsed_seconds`
|
|
181
|
-
|Time elapsed since start
|
|
182
|
-
|
|
183
|
-
|`eta_seconds`
|
|
184
|
-
|Estimated seconds until completion
|
|
185
|
-
|
|
186
|
-
|`active_threads`
|
|
187
|
-
|Number of active worker threads (parallel mode)
|
|
188
|
-
|===
|
|
189
|
-
|
|
190
|
-
=== Computed Properties
|
|
191
|
-
|
|
192
|
-
[source,ruby]
|
|
193
|
-
----
|
|
194
|
-
progress.percentage # => 45.2
|
|
195
|
-
progress.files_remaining # => 27
|
|
196
|
-
progress.bytes_remaining # => 125829120
|
|
197
|
-
progress.elapsed_formatted # => "2m 15s"
|
|
198
|
-
progress.eta_formatted # => "3m 05s"
|
|
199
|
-
progress.completion_time # => 2024-01-15 14:32:10
|
|
51
|
+
tracker = Omnizip::Progress.track(
|
|
52
|
+
total_files: 100,
|
|
53
|
+
total_bytes: 1_000_000,
|
|
54
|
+
reporter: :console
|
|
55
|
+
)
|
|
200
56
|
----
|
|
201
57
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
=== Track Processing Rate
|
|
58
|
+
=== Log Reporter
|
|
205
59
|
|
|
206
|
-
|
|
60
|
+
Write updates to a log file (pass the reporter instance):
|
|
207
61
|
|
|
208
62
|
[source,ruby]
|
|
209
63
|
----
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
rates << progress.throughput_mb_s
|
|
214
|
-
|
|
215
|
-
# Alert if speed drops significantly
|
|
216
|
-
if rates.size > 10 && progress.throughput_mb_s < rates[-10..-1].average * 0.5
|
|
217
|
-
puts "WARNING: Processing slowed down significantly"
|
|
218
|
-
puts "Current file: #{progress.current_file}"
|
|
219
|
-
end
|
|
220
|
-
end
|
|
64
|
+
reporter = Omnizip::Progress::LogReporter.new(
|
|
65
|
+
log_file: 'compress.log', verbose: true
|
|
66
|
+
)
|
|
221
67
|
|
|
222
|
-
|
|
223
|
-
|
|
68
|
+
tracker = Omnizip::Progress.track(
|
|
69
|
+
total_files: 100,
|
|
70
|
+
total_bytes: 1_000_000,
|
|
71
|
+
reporter: reporter
|
|
72
|
+
)
|
|
224
73
|
----
|
|
225
74
|
|
|
226
|
-
|
|
75
|
+
== Wrapping a Compression Job
|
|
227
76
|
|
|
228
|
-
|
|
77
|
+
Track a directory compression file by file:
|
|
229
78
|
|
|
230
79
|
[source,ruby]
|
|
231
80
|
----
|
|
232
|
-
|
|
81
|
+
files = Dir.glob('data/**/*').select { |f| File.file?(f) }
|
|
82
|
+
tracker = Omnizip::Progress.track(
|
|
83
|
+
total_files: files.size,
|
|
84
|
+
total_bytes: files.sum { |f| File.size(f) },
|
|
85
|
+
reporter: :console
|
|
86
|
+
)
|
|
233
87
|
|
|
234
|
-
Omnizip.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
duration: progress.current_file_duration,
|
|
239
|
-
size: progress.current_file_size
|
|
240
|
-
}
|
|
88
|
+
Omnizip::Archive.create('backup.zip') do |archive|
|
|
89
|
+
files.each do |file|
|
|
90
|
+
archive.add_file(file, file.delete_prefix('data/'))
|
|
91
|
+
tracker.update(files: 1, bytes: File.size(file), current_file: file)
|
|
241
92
|
end
|
|
242
93
|
end
|
|
243
|
-
|
|
244
|
-
slow_files.sort_by { |f| -f[:duration] }.first(10).each do |f|
|
|
245
|
-
puts "#{f[:name]}: #{f[:duration]}s (#{f[:size]} bytes)"
|
|
246
|
-
end
|
|
247
94
|
----
|
|
248
95
|
|
|
249
|
-
==
|
|
250
|
-
|
|
251
|
-
=== Log File Reporter
|
|
96
|
+
== Web Application Integration
|
|
252
97
|
|
|
253
|
-
|
|
98
|
+
Broadcast progress from a background job:
|
|
254
99
|
|
|
255
100
|
[source,ruby]
|
|
256
101
|
----
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
log.flush
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
----
|
|
264
|
-
|
|
265
|
-
=== JSON Progress Reporter
|
|
266
|
-
|
|
267
|
-
Output machine-readable progress:
|
|
102
|
+
class ArchiveJob < ApplicationJob
|
|
103
|
+
def perform(user_id, file_paths)
|
|
104
|
+
tracker = Omnizip::Progress.track(total_files: file_paths.size)
|
|
268
105
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
json = {
|
|
273
|
-
timestamp: Time.now.iso8601,
|
|
274
|
-
percentage: progress.percentage,
|
|
275
|
-
files_done: progress.files_processed,
|
|
276
|
-
files_total: progress.total_files,
|
|
277
|
-
current: progress.current_file,
|
|
278
|
-
rate_mbs: progress.throughput_mb_s,
|
|
279
|
-
eta_seconds: progress.eta_seconds
|
|
280
|
-
}
|
|
106
|
+
file_paths.each_with_index do |path, i|
|
|
107
|
+
process_file(path)
|
|
108
|
+
tracker.update(files: 1, current_file: path)
|
|
281
109
|
|
|
282
|
-
|
|
110
|
+
ActionCable.server.broadcast("archive_#{user_id}", {
|
|
111
|
+
status: 'processing',
|
|
112
|
+
files_done: i + 1,
|
|
113
|
+
total_files: file_paths.size,
|
|
114
|
+
percentage: tracker.percentage
|
|
115
|
+
})
|
|
116
|
+
end
|
|
117
|
+
end
|
|
283
118
|
end
|
|
284
119
|
----
|
|
285
120
|
|
|
286
|
-
== CLI Progress Display
|
|
287
|
-
|
|
288
|
-
[source,bash]
|
|
289
|
-
----
|
|
290
|
-
# Default progress bar
|
|
291
|
-
$ omnizip archive create backup.zip files/
|
|
292
|
-
[=========> ] 45% (230/512 files) - 2.5 MB/s - ETA: 1m 23s
|
|
293
|
-
|
|
294
|
-
# Verbose progress
|
|
295
|
-
$ omnizip archive create backup.zip files/ --progress verbose
|
|
296
|
-
Processing: src/app.rb (230/512 files, 45%)
|
|
297
|
-
Size: 45 KB -> 12 KB (73% compression)
|
|
298
|
-
Rate: 2.5 MB/s, Elapsed: 1m 12s, ETA: 1m 23s
|
|
299
|
-
|
|
300
|
-
# Quiet mode (no progress)
|
|
301
|
-
$ omnizip archive create backup.zip files/ --quiet
|
|
302
|
-
|
|
303
|
-
# JSON progress output
|
|
304
|
-
$ omnizip archive create backup.zip files/ --progress json
|
|
305
|
-
{"percentage":45.0,"files":230,"total":512,"eta":83}
|
|
306
|
-
----
|
|
307
|
-
|
|
308
|
-
== Best Practices
|
|
309
|
-
|
|
310
|
-
. **Update Interval**: Don't update too frequently (0.5-1s is good)
|
|
311
|
-
. **Non-Blocking**: Use threads/async for long operations
|
|
312
|
-
. **User Experience**: Always show progress for operations >5 seconds
|
|
313
|
-
. **Error Handling**: Continue tracking even if individual files fail
|
|
314
|
-
. **Resource Cleanup**: Ensure progress callbacks don't leak memory
|
|
315
|
-
|
|
316
121
|
== See Also
|
|
317
122
|
|
|
318
|
-
* link:
|
|
319
|
-
* link:
|
|
320
|
-
* link:../performance-tuning.html[Performance Tuning] - Optimize based on progress data
|
|
123
|
+
* link:streaming.html[Streaming Operations] - Memory-efficient processing
|
|
124
|
+
* link:parallel-processing.html[Parallel Processing] - Multi-core strategies
|
|
@@ -7,47 +7,23 @@ grand_parent: Guides
|
|
|
7
7
|
|
|
8
8
|
== Purpose
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
== Benefits
|
|
13
|
-
|
|
14
|
-
* **Memory Efficient**: Process multi-GB archives with minimal memory footprint
|
|
15
|
-
* **Fast Response**: Start processing immediately without waiting for complete download
|
|
16
|
-
* **Scalable**: Handle archives of any size on servers with limited resources
|
|
17
|
-
* **Web-Friendly**: Perfect for Rails applications and API services
|
|
18
|
-
|
|
19
|
-
== When to Use Streaming
|
|
20
|
-
|
|
21
|
-
[cols="1,3"]
|
|
22
|
-
|===
|
|
23
|
-
|Scenario |Streaming Benefit
|
|
24
|
-
|
|
25
|
-
|Large Archives (>100MB)
|
|
26
|
-
|Avoid loading entire archive into memory
|
|
27
|
-
|
|
28
|
-
|Web Applications
|
|
29
|
-
|Process uploads without temporary files
|
|
30
|
-
|
|
31
|
-
|API Services
|
|
32
|
-
|Stream responses directly to clients
|
|
10
|
+
== Purpose
|
|
33
11
|
|
|
34
|
-
|
|
35
|
-
|Process on resource-constrained servers
|
|
12
|
+
Streaming operations allow you to process archives without loading entire files into memory. This is essential for handling large archives, web applications, and memory-constrained environments.
|
|
36
13
|
|
|
37
|
-
|
|
38
|
-
|Start processing while downloading
|
|
39
|
-
|===
|
|
14
|
+
Omnizip's streaming classes live under +Omnizip::Zip+ (streaming is a ZIP feature; the 7z and TAR writers accept buffered adds through the link:../creating-archives.html[Archive facade]).
|
|
40
15
|
|
|
41
16
|
== Creating Archives with Streaming
|
|
42
17
|
|
|
43
18
|
=== Stream to File
|
|
44
19
|
|
|
45
|
-
Create archives by streaming entries one at a time:
|
|
20
|
+
Create ZIP archives by streaming entries one at a time:
|
|
46
21
|
|
|
47
22
|
[source,ruby]
|
|
48
23
|
----
|
|
49
|
-
|
|
50
|
-
|
|
24
|
+
require 'omnizip/zip/output_stream'
|
|
25
|
+
|
|
26
|
+
Omnizip::Zip::OutputStream.open('backup.zip') do |stream|
|
|
51
27
|
Dir.glob('logs/*.log').each do |file|
|
|
52
28
|
stream.put_next_entry(File.basename(file))
|
|
53
29
|
File.open(file, 'rb') do |f|
|
|
@@ -57,22 +33,31 @@ Omnizip::OutputStream.open('backup.zip', format: :zip) do |stream|
|
|
|
57
33
|
end
|
|
58
34
|
----
|
|
59
35
|
|
|
36
|
+
Per-entry compression method and level are supported:
|
|
37
|
+
|
|
38
|
+
[source,ruby]
|
|
39
|
+
----
|
|
40
|
+
stream.put_next_entry('data.bin', compression: :zstandard, level: 3)
|
|
41
|
+
----
|
|
42
|
+
|
|
60
43
|
=== Stream to Memory
|
|
61
44
|
|
|
62
|
-
Create archives in memory
|
|
45
|
+
Create ZIP archives in memory with +Omnizip::Buffer+:
|
|
63
46
|
|
|
64
47
|
[source,ruby]
|
|
65
48
|
----
|
|
66
|
-
zip_data = Omnizip::
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
stream.put_next_entry('data.json')
|
|
71
|
-
stream.write(export_data.to_json)
|
|
49
|
+
zip_data = Omnizip::Buffer.create(:zip) do |archive|
|
|
50
|
+
archive.add('report.txt', generate_report)
|
|
51
|
+
archive.add('data.json', export_data.to_json)
|
|
72
52
|
end
|
|
73
53
|
|
|
74
|
-
|
|
75
|
-
|
|
54
|
+
send_data zip_data.read, filename: 'export.zip' # StringIO
|
|
55
|
+
|
|
56
|
+
# Or straight from a Hash
|
|
57
|
+
zip_data = Omnizip::Buffer.create_from_hash(
|
|
58
|
+
'report.txt' => generate_report,
|
|
59
|
+
'data.json' => export_data.to_json
|
|
60
|
+
)
|
|
76
61
|
----
|
|
77
62
|
|
|
78
63
|
== Reading Archives with Streaming
|
|
@@ -83,11 +68,12 @@ Process each file as it's read from the archive:
|
|
|
83
68
|
|
|
84
69
|
[source,ruby]
|
|
85
70
|
----
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
require 'omnizip/zip/input_stream'
|
|
72
|
+
|
|
73
|
+
Omnizip::Zip::InputStream.open('large-archive.zip') do |stream|
|
|
74
|
+
while (entry = stream.get_next_entry)
|
|
88
75
|
next if entry.directory?
|
|
89
76
|
|
|
90
|
-
# Process each file immediately
|
|
91
77
|
case entry.name
|
|
92
78
|
when /\.csv$/
|
|
93
79
|
import_csv_data(stream.read)
|
|
@@ -98,21 +84,16 @@ Omnizip::InputStream.open('large-archive.zip') do |stream|
|
|
|
98
84
|
end
|
|
99
85
|
----
|
|
100
86
|
|
|
101
|
-
===
|
|
87
|
+
=== Extract to Memory
|
|
102
88
|
|
|
103
|
-
|
|
89
|
+
Read all entries into a Hash without touching the filesystem:
|
|
104
90
|
|
|
105
91
|
[source,ruby]
|
|
106
92
|
----
|
|
107
|
-
Omnizip::
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
File.open(entry.name, 'wb') do |f|
|
|
112
|
-
f.write(stream.read)
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
93
|
+
files = Omnizip::Buffer.extract_to_memory(File.binread('backup.zip'))
|
|
94
|
+
|
|
95
|
+
files.each do |filename, content|
|
|
96
|
+
process_file(filename, content)
|
|
116
97
|
end
|
|
117
98
|
----
|
|
118
99
|
|
|
@@ -120,19 +101,15 @@ end
|
|
|
120
101
|
|
|
121
102
|
=== Rails Upload Processing
|
|
122
103
|
|
|
123
|
-
Process uploaded archives without temporary files:
|
|
124
|
-
|
|
125
104
|
[source,ruby]
|
|
126
105
|
----
|
|
127
106
|
class ArchiveController < ApplicationController
|
|
128
107
|
def upload
|
|
129
108
|
uploaded = params[:archive]
|
|
130
109
|
|
|
131
|
-
|
|
132
|
-
files = Omnizip::Buffer.extract_all_to_memory(uploaded.read)
|
|
110
|
+
files = Omnizip::Buffer.extract_to_memory(uploaded.read)
|
|
133
111
|
|
|
134
112
|
files.each do |filename, content|
|
|
135
|
-
# Process each file
|
|
136
113
|
process_file(filename, content)
|
|
137
114
|
end
|
|
138
115
|
|
|
@@ -143,70 +120,23 @@ end
|
|
|
143
120
|
|
|
144
121
|
=== API Response Streaming
|
|
145
122
|
|
|
146
|
-
Generate and stream archives in API responses:
|
|
147
|
-
|
|
148
123
|
[source,ruby]
|
|
149
124
|
----
|
|
150
125
|
get '/export' do
|
|
151
126
|
content_type 'application/zip'
|
|
152
127
|
attachment 'export.zip'
|
|
153
128
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
zip.put_next_entry("#{user.id}.json")
|
|
158
|
-
zip.write(user.to_json)
|
|
159
|
-
end
|
|
129
|
+
zip_data = Omnizip::Buffer.create(:zip) do |archive|
|
|
130
|
+
User.find_each do |user|
|
|
131
|
+
archive.add("#{user.id}.json", user.to_json)
|
|
160
132
|
end
|
|
161
|
-
|
|
162
|
-
out << buffer.string
|
|
163
133
|
end
|
|
134
|
+
|
|
135
|
+
zip_data.read
|
|
164
136
|
end
|
|
165
137
|
----
|
|
166
138
|
|
|
167
|
-
== Performance Characteristics
|
|
168
|
-
|
|
169
|
-
[cols="2,1,1,1"]
|
|
170
|
-
|===
|
|
171
|
-
|Operation |Traditional |Streaming |Improvement
|
|
172
|
-
|
|
173
|
-
|100MB Archive
|
|
174
|
-
|100MB RAM
|
|
175
|
-
|~10MB RAM
|
|
176
|
-
|90% reduction
|
|
177
|
-
|
|
178
|
-
|1GB Archive
|
|
179
|
-
|1GB RAM
|
|
180
|
-
|~10MB RAM
|
|
181
|
-
|99% reduction
|
|
182
|
-
|
|
183
|
-
|Web Upload
|
|
184
|
-
|Temp file required
|
|
185
|
-
|No temp file
|
|
186
|
-
|Faster response
|
|
187
|
-
|
|
188
|
-
|Large Extraction
|
|
189
|
-
|Full parse first
|
|
190
|
-
|Start immediately
|
|
191
|
-
|Better UX
|
|
192
|
-
|===
|
|
193
|
-
|
|
194
|
-
== Best Practices
|
|
195
|
-
|
|
196
|
-
. **Use Streaming for Large Files**: Any archive >50MB should use streaming
|
|
197
|
-
. **Process Incrementally**: Don't collect all data before processing
|
|
198
|
-
. **Handle Errors Gracefully**: Stream operations can fail mid-process
|
|
199
|
-
. **Clean Up Resources**: Always use blocks to ensure cleanup
|
|
200
|
-
. **Monitor Memory**: Verify memory stays constant during processing
|
|
201
|
-
|
|
202
|
-
== Limitations
|
|
203
|
-
|
|
204
|
-
* Cannot seek backwards in stream (forward-only processing)
|
|
205
|
-
* Some operations require random access (use file-based API for those)
|
|
206
|
-
* Central directory must fit in memory (usually small)
|
|
207
|
-
|
|
208
139
|
== See Also
|
|
209
140
|
|
|
210
|
-
* link:parallel-processing.html[Parallel Processing] -
|
|
211
|
-
* link
|
|
212
|
-
* link:../../compatibility.html[Compatibility] - Streaming support across formats
|
|
141
|
+
* link:parallel-processing.html[Parallel Processing] - Multi-core strategies
|
|
142
|
+
* link:../creating-archives.html[Creating Archives] - Block-style archive facade
|
|
@@ -59,7 +59,8 @@ Omnizip.compress_file('data.txt', 'data.txt.bz2',
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
# Create archive with BZip2 compression
|
|
62
|
-
Omnizip::Archive.create('backup.
|
|
62
|
+
Omnizip::Archive.create('backup.7z', format: :seven_zip,
|
|
63
|
+
algorithm: :bzip2) do |archive|
|
|
63
64
|
archive.add_directory('project/')
|
|
64
65
|
end
|
|
65
66
|
----
|
|
@@ -59,10 +59,8 @@ Deflate is the standard ZIP compression algorithm, offering a good balance betwe
|
|
|
59
59
|
# Deflate is the default for ZIP
|
|
60
60
|
Omnizip.compress_directory('project/', 'backup.zip')
|
|
61
61
|
|
|
62
|
-
#
|
|
62
|
+
# ZIP entries use Deflate by default
|
|
63
63
|
Omnizip::Archive.create('archive.zip', format: :zip) do |archive|
|
|
64
|
-
archive.compression = :deflate
|
|
65
|
-
archive.level = 6 # Default level
|
|
66
64
|
archive.add_directory('files/')
|
|
67
65
|
end
|
|
68
66
|
----
|
|
@@ -250,8 +248,6 @@ Omnizip.compress_directory(
|
|
|
250
248
|
----
|
|
251
249
|
# Package for distribution (broad compatibility)
|
|
252
250
|
Omnizip::Archive.create('release-v1.0.zip', format: :zip) do |archive|
|
|
253
|
-
archive.compression = :deflate
|
|
254
|
-
archive.level = 9 # Best compression
|
|
255
251
|
archive.add_directory('dist/')
|
|
256
252
|
end
|
|
257
253
|
----
|
|
@@ -265,8 +261,6 @@ Process uploads quickly:
|
|
|
265
261
|
# Handle user uploads efficiently
|
|
266
262
|
uploaded_files.each do |file|
|
|
267
263
|
Omnizip::Archive.create("#{user_id}-upload.zip", format: :zip) do |archive|
|
|
268
|
-
archive.compression = :deflate
|
|
269
|
-
archive.level = 6 # Balanced
|
|
270
264
|
archive.add_data(file.original_filename, file.read)
|
|
271
265
|
end
|
|
272
266
|
end
|