Last active
September 4, 2015 14:25
-
-
Save epitron/336195 to your computer and use it in GitHub Desktop.
A Ruby class for compressing and streaming ZIP files on the fly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'epitools' | |
class StreamedZip | |
## TODO: Use arrays instead of string munging, then join results. (Wrapper class! (Pathname?)) | |
def initialize(paths) | |
@paths = paths | |
end | |
def stream(opts={}, &block) | |
paths = @paths | |
parent_dirs = opts[:parent_dirs] || 1 | |
chunk_size = opts[:chunk_size] || 25_000 | |
puts "* Streaming zip (#{paths.size} paths)..." | |
root = common_root(paths, parent_dirs) | |
puts " |_ common root: #{root.inspect}" | |
puts " |_ chunk size: #{chunk_size} bytes" | |
#open streams | |
#out = open "test.zip", "wb" | |
command = %{cd "#{root}"; zip -r -X -0 -@ -} | |
puts " |_ command: #{command.inspect}" | |
stdin, stdout, stderr = Open3.popen3(command) | |
#inject list | |
puts " |_ writing paths..." | |
paths.map!{|path| relative_path(root, path)} | |
paths.each{|path| stdin.puts(path); puts " |_ #{path}"} | |
stdin.close | |
#read data | |
puts " |_ streaming..." | |
loop do | |
data = stdout.read(chunk_size) | |
break unless data | |
#out.write(data) | |
yield data | |
end | |
puts | |
#show zip output | |
puts " |_ done!" | |
return stderr.read | |
end | |
def common_root(paths, parent_dirs) | |
split_paths = paths.map{|p| p.split('/')} | |
common = [] | |
n = 0 | |
loop do | |
pieces = split_paths.map{|p| p[n]} | |
first = pieces.first | |
all_equal = pieces[1..-1].all?{|p| p == first} | |
if all_equal | |
common << first | |
else | |
break | |
end | |
n += 1 | |
end | |
parent_dirs.times { common.pop } | |
File.join(*common) | |
end | |
def relative_path(common_root, path) | |
if path.starts_with?(common_root) | |
path.gsub(common_root + "/", '') | |
else | |
raise "Doesn't contain common root: #{path} (root: #{common_root})" | |
end | |
end | |
def close | |
end | |
end | |
if $0 == __FILE__ | |
require 'pp' | |
#collect files | |
base = "/home/epi/ezelf/test/albums" | |
dir = Dir["#{base}/*"].first | |
filelist = Dir[ "#{dir}/*" ] | |
pp filelist | |
puts "---------" | |
s = StreamedZip.new | |
s.stream(filelist) do |chunk| | |
print "." | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "streamed_zip" | |
headers["Content-Type"] = 'application/zip' | |
zip = StreamedZip.new(paths) | |
# In your controller: | |
render :text => proc { |response, output| | |
zip.stream do |chunk| | |
output.write(chunk) | |
#output.flush | |
end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment