https://github.com/fringd/zipline
在Rails 3.1.1中,jo5h的答案对我没有用。
但我找到了一个YouTube视频来帮助我。
http://www.youtube.com/watch?v=K0XvnspdPsc
关键是创建一个响应each方法的对象... 这是我所做的:
class ZipGenerator
def initialize(model)
@model = model
end
def each( &block )
output = Object.new
output.define_singleton_method :tell, Proc.new { 0 }
output.define_singleton_method :pos=, Proc.new { |x| 0 }
output.define_singleton_method :<<, Proc.new { |x| block.call(x) }
output.define_singleton_method :close, Proc.new { nil }
Zip::IoZip.open(output) do |zip|
@model.attachments.all.each do |attachment|
zip.put_next_entry "#{attachment.name}.pdf"
file = attachment.file.file.send :file
file = File.open(file) if file.is_a? String
while buffer = file.read(2048)
zip << buffer
end
end
end
sleep 10
end
end
def getzip
self.response_body = ZipGenerator.new(@model)
#this is a hack to preven middleware from buffering
headers['Last-Modified'] = Time.now.to_s
end
编辑:
上面的解决方案实际上并没有起作用...问题在于RubyZip需要在进行条目头部的重写时跳转到文件中。特别是,它需要在写入数据之前写入压缩大小。在真正的流式情况下这是不可能的...所以最终这个任务可能是不可能完成的。有可能一次性缓冲整个文件,但这似乎不值得去做。最终我只是写到了一个临时文件里...在Heroku上,我可以将文件写入Rails.root/tmp,虽然反馈不够即时,也不是一个理想的解决办法,但是必要的。
另一个编辑:
我最近又想到了一个想法...如果我们不压缩文件,我们就能知道文件的压缩大小。计划大致如下:
将ZipStreamOutput类作为子类实现以下功能:
我还没有尝试实现这个,但如果成功了,我会报告回来。
好吧,最后一次编辑:
在Zip标准中:http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
他们提到有一个位可以翻转,将大小,压缩大小和CRC放在文件之后。所以我的新计划是子类化zipoutput流,使其
此外,我需要解决所有关于Rails的流式输出的问题...
总之这一切都奏效了!
这里有一个gem!
我有一个类似的问题。我不需要直接进行流传输,但只是遇到了第一种情况,即不想编写临时文件。您可以轻松修改ZipOutputStream以接受IO对象而不仅仅是文件名。
module Zip
class IOOutputStream < ZipOutputStream
def initialize io
super '-'
@outputStream = io
end
def stream
@outputStream
end
end
end
接下来,只需要在您的Proc中使用新的Zip::IOOutputStream即可。在您的控制器中,您可能会这样做:
self.response_body = proc do |response, output|
Zip::IOOutputStream.open(output) do |zip|
my_files.each do |file|
zip.put_next_entry file
zip << IO.read file
end
end
end
class SomeController < ApplicationController
def some_action
compressed_filestream = Zip::ZipOutputStream.write_buffer do |zos|
zos.put_next_entry "some/filename.ext"
zos.print data
end
compressed_filestream .rewind
respond_to do |format|
format.zip do
send_data compressed_filestream .read, filename: "some.zip"
end
end
# or some other return of send_data
end
end
http://info.michael-simons.eu/2008/01/21/using-rubyzip-to-create-zip-files-on-the-fly/
它使用ZipOutputStream构建和生成zip文件,然后使用send_file直接从控制器发送出去。
使用分块的HTTP传输编码进行输出:HTTP头“Transfer-Encoding: chunked”,并根据分块编码规范重新构造输出,因此在传输开始时无需知道结果ZIP文件的大小。可以借助Open3.popen3和线程轻松地在Ruby中编写。
ZipOutputStream#update_local_headers
,从ZipOutputStream#close
调用)。因此,在操作完成之前,使用ZipOutputStream服务数据块是不可能的。 - Rômulo Ceccon