1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
class ZipService
class Subdir < Struct.new(:name, :stream, :spurious)
end
attr_reader :current_key, :current_output, :current_spurious, :yielder
def initialize data
@zip_data = StringIO.new(data)
@current_key = nil
@current_output = nil
end
def subdirs
Enumerator.new do |yielder|
@yielder = yielder
Zip::File.open_buffer(@zip_data, &(method :_subdirs))
end
end
def _subdirs zip_file
zip_file.each do | entry |
add_entry entry
end
finish_current_output
end
def add_entry entry
key = entry_key entry
unless key == current_key
finish_current_output
open_new_output key
end
add_to_current_output entry
end
def add_to_current_output entry
return if is_spurious! entry.name
current_output.put_next_entry entry.name
write_to_current_output entry.get_input_stream
end
def write_to_current_output input_stream
# the condition below is true for directory entries
return if Zip::NullInputStream == input_stream
current_output.write input_stream.read
end
def finish_current_output
if current_output
@yielder << Subdir.new(
current_key,
# Second part of the solution, yield the closed stream
current_output.close_buffer,
current_spurious)
end
end
def open_new_output entry_key
@current_key = entry_key
# First piece of the solution, use internal way to create a Zip::OutputStream
@current_output = Zip::OutputStream.new(StringIO.new(''), true, nil)
@current_spurious = []
end
def entry_key entry
# last dir name File.dirname.split("/").last
entry.name.split('/', -1)[-2]
end
def is_spurious! entry_name
segments = entry_name.split('/', 3)
return false if segments.size < 3
current_spurious << segments.second
return true
end
end
|