我已经在内存中生成了许多PDF文件,我想在将其作为电子邮件附件发送之前将它们压缩成一个zip文件。我查看了Rubyzip,但它似乎不允许我创建一个不保存到磁盘的zip文件(也许我错了)。
有没有办法在不创建临时文件的情况下压缩这些文件?
我曾经遇到相似的问题,使用rubyzip gem和stringio对象解决了它。
事实证明rubyzip提供了一个返回stringio对象的方法:ZipOutputStream.write_buffer
。
你可以使用put_next_entry
创建zip文件结构并写入内容,完成后可以倒回stringio并使用sysread读取二进制数据。
请参考以下简单示例(适用于rubyzip 0.9.X):
require 'zip/zip'
stringio = Zip::OutputStream.write_buffer do |zio|
zio.put_next_entry("test.txt")
zio.write "Hello world!"
end
stringio.rewind
binary_data = stringio.sysread
在jruby 1.6.5.1(ruby-1.9.2-p136)(2011-12-27 1bf37c2) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_29) [Windows Server 2008-amd64-java]上测试通过。
以下示例适用于rubyzip >= 1.0.0
require 'rubygems'
require 'zip'
stringio = Zip::OutputStream.write_buffer do |zio|
zio.put_next_entry("test.txt")
zio.write "Hello world!"
end
binary_data = stringio.string
在 jruby 1.7.22 (1.9.3p551) 2015-08-20 c28f492 on OpenJDK 64-Bit Server VM 1.7.0_79-b14 +jit [linux-amd64] 上测试通过,并使用 rubyzip gem 1.1.7。
我找到了两个RubyZip库。
Chilkat的库绝对允许我们在内存中创建zip文件,而不像这些链接中自动将其写入磁盘:Zip to Memory, Zip from in memory data
另一方面,在SourceForge上可能提供了在内存中压缩文件的选项,但我不是完全确定,因为我对ruby非常陌生。 SourceForge的rubyzip基于java.util.zip
,这导致它有一个名为ZipOutputStream
的类。我不知道rubyzip实现有多好,但使用java.util.zip
实现,可以将OutputStream
设置为ByteArrayOutputStream
、FileOutputStream
、FilterOutputStream
、ObjectOutputStream
、OutputStream
、PipedOutputStream
等。ZipOutputStream
将某种类型的ByteArrayOutputStream
传递进去就可以将其输出到内存中。
如果在rubyzip中不存在,那么你可以编写自己的实现,并提交给rubyzip进行开源包含。
如果你使用的是Linux系统,并且取决于你有多少RAM以及你的文件有多大,你可以使用tmpfs(共享内存)。这样,rubyzip基于磁盘的方法就可以工作了。http://www.mjmwired.net/kernel/Documentation/filesystems/tmpfs.txt
接受的答案效果很好,但并没有解决我的问题。我不想使用 write_buffer方法,因为它在块关闭后自动关闭流。下面的代码片段让你更加控制流何时创建和关闭。
require 'stringio'
require 'zip'
io = StringIO.new
zip_io = Zip::OutputStream.new(io, true) # 'true' indicates 'io' is a stream
zip_io.put_next_entry('test.txt')
zip_io.write('Hello world!')
# Read the data and close the streams
io.rewind
binary_data = io.read
zip_io.close_buffer
io.close