zipping 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c6d26ef58bcb6f2cf5e72b4979157edcacfc2b15
4
+ data.tar.gz: 934f22728e3df8c6aebb2cb5233512a693267e84
5
+ SHA512:
6
+ metadata.gz: 91db69bf6bb30b3573d4ac116b17e3c53d3f1559c504aa67a96236b2687fdf860592d4b150c6aa5339fd28df2dbd463c1cacb0b229889ade0802b0403ad71408
7
+ data.tar.gz: 24796b591dc42fa83ab0cff4afe6646b1cc355ddaa78fbe0bc38c319a619c65a571256a8db3395ad17284a41a08f6682ea3306343a0eeb78ba09d9ff527ed0ae
data/.gitignore ADDED
@@ -0,0 +1,51 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ doc
10
+ .yardoc
11
+
12
+ # bundler
13
+ .bundle
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
+ #
20
+ # * Create a file at ~/.gitignore
21
+ # * Include files you want ignored
22
+ # * Run: git config --global core.excludesfile ~/.gitignore
23
+ #
24
+ # After doing this, these files will be ignored in all your git projects,
25
+ # saving you from having to 'pollute' every project you touch with them
26
+ #
27
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28
+ #
29
+ # For MacOS:
30
+ #
31
+ #.DS_Store
32
+
33
+ # For TextMate
34
+ #*.tmproj
35
+ #tmtags
36
+
37
+ # For emacs:
38
+ #*~
39
+ #\#*
40
+ #.\#*
41
+
42
+ # For vim:
43
+ #*.swp
44
+
45
+ # For redcar:
46
+ #.redcar
47
+
48
+ # For rubinius:
49
+ #*.rbc
50
+
51
+ /.DS_Store
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zipping.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ zipping (0.2.1)
5
+ rubyzip (>= 1.0.0)
6
+ zip-zip (~> 0.2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ rake (10.0.3)
12
+ rubyzip (1.1.0)
13
+ zip-zip (0.2)
14
+ rubyzip (>= 1.0.0)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ bundler (~> 1.3)
21
+ rake
22
+ zipping!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Misaki Kawamura
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ zipping
2
+ =======
3
+
4
+ This gem is for compressing files as a zip and outputting to a stream (or a stream-like interface object). The output to a stream proceeds little by little, as files are compressed.
5
+
6
+ Getting Started
7
+ --
8
+ Add the following line to your Gemfile:
9
+
10
+ gem 'zipping'
11
+
12
+ Usage
13
+ --
14
+ simple:
15
+
16
+ ```ruby
17
+ require 'zipping'
18
+
19
+ Zipping.files_to_zip my_stream, '/path/to/file'
20
+ ```
21
+
22
+ You can pass multiple files.
23
+
24
+ ```ruby
25
+ Zipping.files_to_zip my_stream2, ['/path/to/file', '/another/path']
26
+ ```
27
+
28
+ If you pass a folder, zipping compresses all files in the folder.
29
+
30
+ ```ruby
31
+ Zipping.files_to_zip my_stream3, ['/path/to/folder', '/path/to/other/file']
32
+ ```
33
+
34
+ For example, you have files below:
35
+
36
+ /text/foo.txt
37
+ /text/bar/baz.txt
38
+ /images/abc.png
39
+
40
+ and you run command:
41
+
42
+ ```ruby
43
+ file = File.open '/my.zip', 'wb'
44
+ Zipping.files_to_zip file, ['/text', '/images/abc.png']
45
+ file.close
46
+ ```
47
+
48
+ Then, you get a zip file, and you find entries below in it.
49
+
50
+ text/
51
+ text/foo.txt
52
+ text/bar/
53
+ text/bar/baz.txt
54
+ abc.png
55
+
56
+ To get binary data of zip instead of saving as a file, prepare an 'ASCII-8bit'-encoded empty String object.
57
+
58
+ ```ruby
59
+ zip_data = ''.force_encoding('ASCII-8bit')
60
+ Zipping.files_to_zip zip_data, ['/text', '/images/abc.png']
61
+ ```
62
+
63
+ Then, you get zip binary data in `zip_data`.
64
+
65
+ Notice
66
+ --
67
+ With older(<1.0.0) rubyzip, use 0.2.0.
68
+
69
+ ---
70
+
71
+ Copyright (c) 2013 [Nekojarashi Inc.](http://www.nekojarashi.com)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.1
@@ -0,0 +1,3 @@
1
+ module Zipping
2
+ VERSION = "0.2.1"
3
+ end
data/lib/zipping.rb ADDED
@@ -0,0 +1,731 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require "zipping/version"
3
+ require 'zip'
4
+
5
+ module Zipping
6
+
7
+
8
+ #
9
+ # Constants
10
+ #
11
+
12
+ ZS_c_pk0102 = 0x02014b50
13
+ ZS_c_pk0304 = 0x04034b50
14
+ ZS_c_pk0506 = 0x06054b50
15
+ ZS_c_pk0708 = 0x08074b50
16
+ ZS_c_ver_dir = 0x000a
17
+ ZS_c_ver_file = 0x0014
18
+ ZS_c_ver_made = 0x0315
19
+ ZS_c_opt_none = 0x0000
20
+ ZS_c_opt_nosize = 0x0008
21
+ ZS_c_comp_deflate = 0x0008
22
+ ZS_c_int2_zero = 0x0000
23
+ ZS_c_int4_zero = 0x00000000
24
+
25
+ ZS_c_oattr_dir = 0x41ed4000
26
+ ZS_c_oattr_file = 0x81a44000
27
+
28
+
29
+
30
+ #
31
+ # API methods
32
+ #
33
+
34
+ def self.files_to_zip(output_stream, files, encoding = :utf8, file_division_size = 1048576)
35
+ builder = ZipBuilder.new output_stream, files, encoding, file_division_size
36
+ builder.pack
37
+ end
38
+
39
+ # Deprecated.
40
+ # Recommended to use 'files_to_zip' instead.
41
+ def self.directory_to_zip(output_stream, target_path, usesjis = true, file_division_size = 1048576)
42
+ self.create_zip_file_with_files_and_directories(output_stream, target_path, usesjis, file_division_size)
43
+ end
44
+
45
+ # Deprecated.
46
+ # Recommended to use 'files_to_zip' instead.
47
+ def self.file_to_zip(output_stream, target_path, usesjis = false, file_division_size = 1048576)
48
+ self.create_zip_file_with_files_and_directories(output_stream, target_path, usesjis, file_division_size)
49
+ end
50
+
51
+
52
+
53
+ #
54
+ # Internal Classes
55
+ #
56
+
57
+ # Output stream wrapper that measures size of stream passing through.
58
+ class StreamMeter
59
+ def initialize(output_stream)
60
+ @output_stream = output_stream
61
+ @size = 0
62
+ end
63
+ def << (data)
64
+ @size += data.length
65
+ @output_stream << data
66
+ end
67
+ attr_reader :size
68
+ end
69
+
70
+ class ZipBuilder
71
+
72
+ # Initialize ZipBuilder.
73
+ # 'files' must be a String(file or directory path), a Hash(entity), or an Array of Strings and/or Hashes.
74
+ def initialize(output_stream, files, encoding = :utf8, file_division_size = 1048576)
75
+ @o = output_stream
76
+ @f = ZipBuilder.to_entities files
77
+ @e = encoding
78
+ @s = file_division_size
79
+ end
80
+
81
+ # Create an Array of entity Hashes.
82
+ def self.to_entities(files)
83
+ if files.is_a? Hash
84
+ ret = [files]
85
+ elsif files.is_a? String
86
+ entity = ZipBuilder.to_entity(files)
87
+ ret = entity.nil? ? [] : [entity]
88
+ elsif files.is_a? Array
89
+ ret = []
90
+ files.each do |f|
91
+ entity = ZipBuilder.to_entity(f)
92
+ ret << entity unless entity.nil?
93
+ end
94
+ else
95
+ ret = []
96
+ end
97
+ ret
98
+ end
99
+
100
+ # Create an entity Hash with a path String
101
+ def self.to_entity(path)
102
+ return path if path.is_a?(Hash)
103
+ return nil unless path.is_a?(String) && File.exists?(path)
104
+ ret = {
105
+ :path => path,
106
+ :name => File.basename(path)
107
+ }
108
+ end
109
+
110
+ # Create ASCII-8bits string. Also convert encoding if needed.
111
+ def self.to_bytes(str, encoding = :utf8)
112
+ unless encoding.nil? || encoding == :utf8
113
+ case encoding
114
+ when :shift_jis
115
+ begin
116
+ str = str.encode 'Shift_JIS', :invalid => :replace, :undef => :replace, :replace => '??'
117
+ rescue => e
118
+ end
119
+ end
120
+ end
121
+ [str].pack('a*')
122
+ end
123
+
124
+ # Pack file and directory entities and output to stream.
125
+ def pack
126
+ return if @f.empty?
127
+
128
+ # directory entities found but not packed
129
+ @pending_dirs = []
130
+
131
+ # current directory
132
+ @current_dir = ''
133
+ @current_dir_created_at = Time.now
134
+
135
+ # data of positions necessary to create headers
136
+ @dp = []
137
+
138
+ o = StreamMeter.new @o
139
+
140
+ # pack file entities, store directory entities into @pending_dirs
141
+ pack_file_entities o
142
+
143
+ # retrieve and pack stored directories
144
+ until @pending_dirs.empty? do
145
+
146
+ current_dir = @pending_dirs.shift
147
+ @current_dir = current_dir[:name] << '/'
148
+ @current_dir_created_at = current_dir[:time]
149
+
150
+ # write directory entry
151
+ pack_directory o, current_dir[:time]
152
+
153
+ begin
154
+ # get files in the directory
155
+ files = Dir.glob(current_dir[:path] + '/*')
156
+
157
+ # pack files, store directories as entities into @pending_dirs
158
+ pack_files o, files, current_dir
159
+ rescue => e
160
+ end
161
+ end
162
+
163
+ # prepare to create headers
164
+ @header_offset = o.size
165
+ o = StreamMeter.new(@o)
166
+
167
+ # write headers
168
+ write_central_dir_headers o
169
+ @header_size = o.size
170
+ write_end_central_dir_header
171
+ end
172
+
173
+ # Pack file entities and output to stream. Directory entities are not packed but stored.
174
+ def pack_file_entities(o = @o, files = @f, dir = @current_dir, dirs = @pending_dirs, data_positions = @dp, encoding = @e, file_division_size = @s)
175
+ files.each do |file|
176
+ next unless file.is_a?(Hash) && file[:path].present?
177
+
178
+ f = file[:path]
179
+ if File.directory? f
180
+ dirs << file
181
+ next
182
+ end
183
+ next unless File.file? f
184
+ data_offset = o.size
185
+ file_path = f
186
+ fname = dir.clone << (file[:name] || File.basename(file_path))
187
+ bfname = ZipBuilder.to_bytes(fname, encoding)
188
+ filetime = Zip::DOSTime.at(file[:time] || File.mtime(file_path))
189
+ filesize = File.size(file_path)
190
+ pk = [
191
+ ZS_c_pk0304,
192
+ ZS_c_ver_file,
193
+ ZS_c_opt_nosize,
194
+ ZS_c_comp_deflate,
195
+ filetime.to_binary_dos_time,
196
+ filetime.to_binary_dos_date,
197
+ ZS_c_int4_zero,
198
+ ZS_c_int4_zero,
199
+ ZS_c_int4_zero,
200
+ bfname.length,
201
+ ZS_c_int2_zero
202
+ ]
203
+ bin = pk.pack('VvvvvvVVVvv')
204
+ o << bin
205
+ bin = bfname
206
+ o << bin
207
+
208
+ m = StreamMeter.new(o)
209
+ d = Zip::Deflater.new(m)
210
+ File.open(file_path) do |f|
211
+ cur_filesize = filesize
212
+ while cur_filesize > 0
213
+ if cur_filesize >= file_division_size
214
+ d << f.read(file_division_size)
215
+ cur_filesize -= file_division_size
216
+ else
217
+ d << f.read(cur_filesize)
218
+ cur_filesize = 0
219
+ end
220
+ end
221
+ end
222
+ d.finish
223
+
224
+ pk = [
225
+ ZS_c_pk0708,
226
+ d.crc,
227
+ m.size,
228
+ d.size
229
+ ]
230
+ bin = pk.pack('VVVV')
231
+ o << bin
232
+ data_positions << {
233
+ :folder => false,
234
+ :file => fname,
235
+ :file_dos_time => filetime.to_binary_dos_time,
236
+ :file_dos_date => filetime.to_binary_dos_date,
237
+ :binary_fname => bfname,
238
+ :offset => data_offset,
239
+ :crc => d.crc,
240
+ :complen => m.size,
241
+ :uncomplen => d.size
242
+ }
243
+ end
244
+ end
245
+
246
+ # Pack directory and output to stream.
247
+ def pack_directory(o = @o, time_created_at = @current_dir_created_at, dir = @current_dir, data_positions = @dp, encoding = @e)
248
+ bdir = ZipBuilder.to_bytes(dir, encoding)
249
+ data_offset = o.size
250
+ filetime = Zip::DOSTime.at(time_created_at);
251
+ filesize = 0
252
+ pk = [
253
+ ZS_c_pk0304,
254
+ ZS_c_ver_dir,
255
+ ZS_c_opt_none,
256
+ ZS_c_comp_deflate,
257
+ filetime.to_binary_dos_time,
258
+ filetime.to_binary_dos_date,
259
+ ZS_c_int4_zero,
260
+ ZS_c_int4_zero,
261
+ ZS_c_int4_zero,
262
+ bdir.length,
263
+ ZS_c_int2_zero
264
+ ]
265
+ bin = pk.pack('VvvvvvVVVvv')
266
+ o << bin
267
+ bin = bdir
268
+ o << bin
269
+ data_positions << {
270
+ :folder => true,
271
+ :file => dir,
272
+ :file_dos_time => filetime.to_binary_dos_time,
273
+ :file_dos_date => filetime.to_binary_dos_date,
274
+ :binary_fname => bdir,
275
+ :offset => data_offset,
276
+ :crc => ZS_c_int4_zero,
277
+ :complen => ZS_c_int4_zero,
278
+ :uncomplen => ZS_c_int4_zero
279
+ }
280
+ end
281
+
282
+ # Pack files and output to stream. Directories are not packed but stored.
283
+ def pack_files(o, files, dir, dirs = @pending_dirs, data_positions = @dp, encoding = @e, file_division_size = @s)
284
+ files.each do |f|
285
+ if File.directory? f
286
+ dirs << {
287
+ :path => f,
288
+ :name => dir[:name] + File.basename(f),
289
+ :time => dir[:time]
290
+ }
291
+ next
292
+ end
293
+ next unless File.file? f
294
+ data_offset = o.size
295
+ file_path = f
296
+ file = dir[:name] + File.basename(file_path)
297
+ bfile = ZipBuilder.to_bytes(file, encoding)
298
+ filetime = Zip::DOSTime.at(dir[:time] || File.mtime(file_path))
299
+ filesize = File.size(file_path)
300
+ pk = [
301
+ ZS_c_pk0304,
302
+ ZS_c_ver_file,
303
+ ZS_c_opt_nosize,
304
+ ZS_c_comp_deflate,
305
+ filetime.to_binary_dos_time,
306
+ filetime.to_binary_dos_date,
307
+ ZS_c_int4_zero,
308
+ ZS_c_int4_zero,
309
+ ZS_c_int4_zero,
310
+ bfile.length,
311
+ ZS_c_int2_zero
312
+ ]
313
+ bin = pk.pack('VvvvvvVVVvv')
314
+ o << bin
315
+ bin = bfile
316
+ o << bin
317
+
318
+ m = StreamMeter.new(o)
319
+ d = Zip::Deflater.new(m)
320
+ File.open(file_path) do |f|
321
+ cur_filesize = filesize
322
+ while cur_filesize > 0
323
+ if cur_filesize >= file_division_size
324
+ d << f.read(file_division_size)
325
+ cur_filesize -= file_division_size
326
+ else
327
+ d << f.read(cur_filesize)
328
+ cur_filesize = 0
329
+ end
330
+ end
331
+ end
332
+ d.finish
333
+
334
+ pk = [
335
+ ZS_c_pk0708,
336
+ d.crc,
337
+ m.size,
338
+ d.size
339
+ ]
340
+ bin = pk.pack('VVVV')
341
+ o << bin
342
+ data_positions << {
343
+ :folder => false,
344
+ :file => file,
345
+ :file_dos_time => filetime.to_binary_dos_time,
346
+ :file_dos_date => filetime.to_binary_dos_date,
347
+ :binary_fname => bfile,
348
+ :offset => data_offset,
349
+ :crc => d.crc,
350
+ :complen => m.size,
351
+ :uncomplen => d.size
352
+ }
353
+ end
354
+ end
355
+
356
+ # Write central directories.
357
+ def write_central_dir_headers(o = @o, data_positions = @dp)
358
+ data_positions.each do |dp|
359
+ pk = [
360
+ ZS_c_pk0102,
361
+ ZS_c_ver_made,
362
+ (dp[:folder] ? ZS_c_ver_dir : ZS_c_ver_file),
363
+ ZS_c_opt_nosize,
364
+ ZS_c_comp_deflate,
365
+ dp[:file_dos_time],
366
+ dp[:file_dos_date],
367
+ dp[:crc],
368
+ dp[:complen],
369
+ dp[:uncomplen],
370
+ dp[:binary_fname].length,
371
+ ZS_c_int2_zero,
372
+ ZS_c_int2_zero,
373
+ ZS_c_int2_zero,
374
+ ZS_c_int2_zero,
375
+ (dp[:folder] ? ZS_c_oattr_dir : ZS_c_oattr_file),
376
+ dp[:offset]
377
+ ]
378
+ bin = pk.pack('VvvvvvvVVVvvvvvVV')
379
+ o << bin
380
+ bin = dp[:binary_fname]
381
+ o << bin
382
+ end
383
+ end
384
+
385
+ # Write end of central directory.
386
+ def write_end_central_dir_header(o = @o, entry_count = @dp.length, dirsize = @header_size, offset = @header_offset)
387
+ pk = [
388
+ ZS_c_pk0506,
389
+ ZS_c_int2_zero,
390
+ ZS_c_int2_zero,
391
+ entry_count,
392
+ entry_count,
393
+ dirsize,
394
+ offset,
395
+ ZS_c_int2_zero
396
+ ]
397
+ o << pk.pack('VvvvvVVv')
398
+ end
399
+ end
400
+
401
+
402
+ #
403
+ # Internal method
404
+ #
405
+
406
+ # Pack files into zip.
407
+ # You must pass an entity Hash (or Array of them) consists of :path(path of file to pack into zip) and :name(file path inside zip) to this method as 'target_files'.
408
+ # Optionally, you may add :time(time created at) to the entity Hashes.
409
+ def self.create_zip_file_with_file_entities(outputStream, target_files, usesjis = true, file_division_size = 1048576)
410
+ begin
411
+
412
+ # prepare entry list for zip
413
+ target_files = [target_files] unless target_files.instance_of? Array
414
+ entries = []
415
+ target_files.each do |file|
416
+ if file.is_a? String
417
+ begin
418
+ entry = {
419
+ :path => file,
420
+ :name => File.basename(file),
421
+ :time => File.mtime(file)
422
+ }
423
+ rescue => e
424
+ next
425
+ end
426
+ elsif file.is_a? Hash
427
+ next unless file[:path].present? && file[:path].is_a?(String)
428
+ entry = file
429
+ path = entry[:path]
430
+ entry[:name] = File.basename(path) unless entry[:name].present? && entry[:name].is_a?(String)
431
+ entry[:time] = File.mtime(path) unless entry[:time].present? && entry[:time].is_a?(Time)
432
+ else
433
+ next
434
+ end
435
+ entries << entry
436
+ end
437
+ return if entries.empty?
438
+
439
+ # prepare to measure stream size
440
+ o = StreamMeter.new(outputStream)
441
+
442
+ # container to collect header info
443
+ data_positions = []
444
+
445
+ # compress entries
446
+ self.compress_entries(o, entries, data_positions, usesjis, file_division_size)
447
+
448
+ # measure stream size
449
+ pk0102_offset = o.size
450
+
451
+ # prepare to measure header size
452
+ m = StreamMeter.new(o)
453
+
454
+ # write headers
455
+ self.create_central_dir_headers(m, data_positions)
456
+
457
+ # measure header size
458
+ pk0102_size = m.size
459
+
460
+ # write tail header
461
+ self.create_end_cent_dir_header(o, data_positions.length, pk0102_size, pk0102_offset)
462
+ rescue => e
463
+ end
464
+ end
465
+
466
+ # Pack files and folders into zip.
467
+ # All files in folders are also packed into zip, and the structure inside folders are conserved.
468
+ def self.create_zip_file_with_files_and_directories(outputStream, target_files, usesjis = true, file_division_size = 1048576)
469
+ begin
470
+
471
+ target_files = [target_files] unless target_files.instance_of? Array
472
+ return if target_files.empty?
473
+
474
+ dirs = []
475
+ o = StreamMeter.new(outputStream)
476
+ data_positions = []
477
+
478
+ self.compress_file_list(o, target_files, "", dirs, data_positions, usesjis, file_division_size)
479
+
480
+ while dirs.length > 0 do
481
+ current_directory = dirs.shift
482
+ dir_path = current_directory[0]
483
+ dir = current_directory[1]
484
+ dir << '/'
485
+ self.create_directory_entry(o, dir_path, dir, data_positions, usesjis, file_division_size)
486
+
487
+ files = Dir.glob(dir_path << '/*')
488
+ self.compress_file_list(o, files, dir, dirs, data_positions, usesjis, file_division_size)
489
+
490
+ end
491
+
492
+ pk0102_offset = o.size
493
+ m = StreamMeter.new(o)
494
+ self.create_central_dir_headers(m, data_positions)
495
+
496
+ self.create_end_cent_dir_header(o, data_positions.length, m.size, pk0102_offset)
497
+ rescue => e
498
+ end
499
+ end
500
+
501
+ # Get file name as ASCII-8bits.
502
+ # If needed, also convert encoding.
503
+ def self.get_binary_fname(str, usesjis)
504
+ if usesjis
505
+ begin
506
+ str = str.encode 'Shift_JIS', :invalid => :replace, :replace => '??'
507
+ rescue => e
508
+ end
509
+ end
510
+ return [str].pack('a*')
511
+ end
512
+
513
+ # Write directory entry.
514
+ #
515
+ def self.create_directory_entry(o, dir_path, dir, data_positions, usesjis = false, file_division_size = 1048576)
516
+ bdir = self.get_binary_fname(dir, usesjis)
517
+ data_offset = o.size
518
+ filetime = Zip::DOSTime.at(File.mtime(dir_path))
519
+ filesize = 0
520
+ pk = [
521
+ ZS_c_pk0304,
522
+ ZS_c_ver_dir,
523
+ ZS_c_opt_none,
524
+ ZS_c_comp_deflate,
525
+ filetime.to_binary_dos_time,
526
+ filetime.to_binary_dos_date,
527
+ ZS_c_int4_zero,
528
+ ZS_c_int4_zero,
529
+ ZS_c_int4_zero,
530
+ bdir.length,
531
+ ZS_c_int2_zero
532
+ ]
533
+ bin = pk.pack('VvvvvvVVVvv')
534
+ o << bin
535
+ bin = bdir
536
+ o << bin
537
+ data_positions << {
538
+ :folder => true,
539
+ :file => dir,
540
+ :file_dos_time => filetime.to_binary_dos_time,
541
+ :file_dos_date => filetime.to_binary_dos_date,
542
+ :binary_fname => bdir,
543
+ :offset => data_offset,
544
+ :crc => ZS_c_int4_zero,
545
+ :complen => ZS_c_int4_zero,
546
+ :uncomplen => ZS_c_int4_zero
547
+ }
548
+ end
549
+
550
+ def self.compress_entries(o, entries, data_positions, usesjis = false, file_division_size = 1048576)
551
+ # `o' must be an output stream which has `size' method
552
+
553
+ entries.each do |entry|
554
+ path = entry[:path]
555
+ name = entry[:name]
556
+ mtime = entry[:time]
557
+
558
+ next if File.directory? path
559
+ next unless File.file? path
560
+ data_offset = o.size
561
+ b_name = self.get_binary_fname(name, usesjis)
562
+ filesize = File.size(path)
563
+ pk = [
564
+ ZS_c_pk0304,
565
+ ZS_c_ver_file,
566
+ ZS_c_opt_nosize,
567
+ ZS_c_comp_deflate,
568
+ mtime.to_binary_dos_time,
569
+ mtime.to_binary_dos_date,
570
+ ZS_c_int4_zero,
571
+ ZS_c_int4_zero,
572
+ ZS_c_int4_zero,
573
+ b_name.length,
574
+ ZS_c_int2_zero
575
+ ]
576
+ bin = pk.pack('VvvvvvVVVvv')
577
+ o << bin
578
+ bin = b_name
579
+ o << bin
580
+
581
+ m = StreamMeter.new(o)
582
+ d = Zip::Deflater.new(m)
583
+ File.open(path) do |f|
584
+ cur_filesize = filesize
585
+ while cur_filesize > 0
586
+ if cur_filesize >= file_division_size
587
+ d << f.read(file_division_size)
588
+ cur_filesize -= file_division_size
589
+ else
590
+ d << f.read(cur_filesize)
591
+ cur_filesize = 0
592
+ end
593
+ end
594
+ end
595
+ d.finish
596
+
597
+ pk = [
598
+ ZS_c_pk0708,
599
+ d.crc,
600
+ m.size,
601
+ d.size
602
+ ]
603
+ bin = pk.pack('VVVV')
604
+ o << bin
605
+ data_positions << {
606
+ :folder => false,
607
+ :file => name,
608
+ :file_dos_time => filetime.to_binary_dos_time,
609
+ :file_dos_date => filetime.to_binary_dos_date,
610
+ :binary_fname => b_name,
611
+ :offset => data_offset,
612
+ :crc => d.crc,
613
+ :complen => m.size,
614
+ :uncomplen => d.size
615
+ }
616
+ end
617
+
618
+ end
619
+
620
+ def self.compress_file_list(o, files, dir, dirs, data_positions, usesjis = false, file_division_size = 1048576)
621
+ files.each do |f|
622
+ if File.directory? f
623
+ dirs << [f, dir.clone << File.basename(f)]
624
+ next
625
+ end
626
+ next unless File.file? f
627
+ data_offset = o.size
628
+ file_path = f
629
+ file = dir.clone << File.basename(file_path)
630
+ bfile = self.get_binary_fname(file, usesjis)
631
+ filetime = Zip::DOSTime.at(File.mtime(file_path))
632
+ filesize = File.size(file_path)
633
+ pk = [
634
+ ZS_c_pk0304,
635
+ ZS_c_ver_file,
636
+ ZS_c_opt_nosize,
637
+ ZS_c_comp_deflate,
638
+ filetime.to_binary_dos_time,
639
+ filetime.to_binary_dos_date,
640
+ ZS_c_int4_zero,
641
+ ZS_c_int4_zero,
642
+ ZS_c_int4_zero,
643
+ bfile.length,
644
+ ZS_c_int2_zero
645
+ ]
646
+ bin = pk.pack('VvvvvvVVVvv')
647
+ o << bin
648
+ bin = bfile
649
+ o << bin
650
+
651
+ m = StreamMeter.new(o)
652
+ d = Zip::Deflater.new(m)
653
+ File.open(file_path) do |f|
654
+ cur_filesize = filesize
655
+ while cur_filesize > 0
656
+ if cur_filesize >= file_division_size
657
+ d << f.read(file_division_size)
658
+ cur_filesize -= file_division_size
659
+ else
660
+ d << f.read(cur_filesize)
661
+ cur_filesize = 0
662
+ end
663
+ end
664
+ end
665
+ d.finish
666
+
667
+ pk = [
668
+ ZS_c_pk0708,
669
+ d.crc,
670
+ m.size,
671
+ d.size
672
+ ]
673
+ bin = pk.pack('VVVV')
674
+ o << bin
675
+ data_positions << {
676
+ :folder => false,
677
+ :file => file,
678
+ :file_dos_time => filetime.to_binary_dos_time,
679
+ :file_dos_date => filetime.to_binary_dos_date,
680
+ :binary_fname => bfile,
681
+ :offset => data_offset,
682
+ :crc => d.crc,
683
+ :complen => m.size,
684
+ :uncomplen => d.size
685
+ }
686
+ end
687
+
688
+ end
689
+
690
+ def self.create_central_dir_headers(o, data_positions)
691
+ data_positions.each do |dp|
692
+ pk = [
693
+ ZS_c_pk0102,
694
+ ZS_c_ver_made,
695
+ (dp[:folder] ? ZS_c_ver_dir : ZS_c_ver_file),
696
+ ZS_c_opt_nosize,
697
+ ZS_c_comp_deflate,
698
+ dp[:file_dos_time],
699
+ dp[:file_dos_date],
700
+ dp[:crc],
701
+ dp[:complen],
702
+ dp[:uncomplen],
703
+ dp[:binary_fname].length,
704
+ ZS_c_int2_zero,
705
+ ZS_c_int2_zero,
706
+ ZS_c_int2_zero,
707
+ ZS_c_int2_zero,
708
+ (dp[:folder] ? ZS_c_oattr_dir : ZS_c_oattr_file),
709
+ dp[:offset]
710
+ ]
711
+ bin = pk.pack('VvvvvvvVVVvvvvvVV')
712
+ o << bin
713
+ bin = dp[:binary_fname]
714
+ o << bin
715
+ end
716
+ end
717
+
718
+ def self.create_end_cent_dir_header(o, entry_count, dirsize, offset)
719
+ pk = [
720
+ ZS_c_pk0506,
721
+ ZS_c_int2_zero,
722
+ ZS_c_int2_zero,
723
+ entry_count,
724
+ entry_count,
725
+ dirsize,
726
+ offset,
727
+ ZS_c_int2_zero
728
+ ]
729
+ o << pk.pack('VvvvvVVv')
730
+ end
731
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'zipping'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Zipping" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
data/zipping.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zipping/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "zipping"
8
+ spec.version = Zipping::VERSION
9
+ spec.authors = ["Shuntaro Shitasako"]
10
+ spec.email = ["info@nekojarashi.com.com"]
11
+ spec.description = "This gem is for compressing files as a zip and outputting to a stream (or a stream-like interface object). The output to a stream proceeds little by little, as files are compressed."
12
+ spec.summary = "Compress files as a zip and output it to a stream."
13
+ spec.homepage = "http://www.nekojarashi.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency("rubyzip", [">= 1.0.0"])
25
+ spec.add_dependency("zip-zip", ["~> 0.2"])
26
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zipping
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Shuntaro Shitasako
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubyzip
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: zip-zip
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.2'
69
+ description: This gem is for compressing files as a zip and outputting to a stream
70
+ (or a stream-like interface object). The output to a stream proceeds little by little,
71
+ as files are compressed.
72
+ email:
73
+ - info@nekojarashi.com.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - .gitignore
79
+ - .rspec
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - VERSION
86
+ - lib/zipping.rb
87
+ - lib/zipping/version.rb
88
+ - spec/spec_helper.rb
89
+ - spec/zipping_spec.rb
90
+ - zipping.gemspec
91
+ homepage: http://www.nekojarashi.com
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.0.3
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Compress files as a zip and output it to a stream.
115
+ test_files:
116
+ - spec/spec_helper.rb
117
+ - spec/zipping_spec.rb