如何在不使用 Ruby 保存到磁盘的情况下生成 zip 文件?
Posted
技术标签:
【中文标题】如何在不使用 Ruby 保存到磁盘的情况下生成 zip 文件?【英文标题】:How can I generate zip file without saving to the disk with Ruby? 【发布时间】:2010-03-09 00:49:56 【问题描述】:我在内存中生成了许多 PDF 文件,我想将它们压缩成一个 zip 文件,然后再将其作为电子邮件附件发送。我看过 Rubyzip,它不允许我创建一个 zip 文件而不将其保存到磁盘(也许我错了)。
有什么方法可以在不创建临时文件的情况下压缩这些文件?
【问题讨论】:
见the answer by @vas,它完全符合您的要求! 【参考方案1】:我有一个类似的问题,我使用 rubyzip gem 和 stringio 对象解决了这个问题。
原来,rubyzip 提供了一个返回 stringio 对象的方法:ZipOutputStream.write_buffer
。
您可以使用put_next_entry
创建您喜欢的 zip 文件结构并写入,完成后您可以倒回字符串并使用 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 在 OpenJDK 64 位服务器 VM 1.7.0_79-b14 +jit [linux-amd64] 和 rubyzip gem 1.1.7 上测试
【讨论】:
谢谢,它有效。但是在我写这个问题的时候,这个方法还不存在。它是在 2011-01-07 中添加的。感谢您的回答,下次我需要压缩内存时,我会知道如何。 LoadError 无法加载此类文件 -- zip 以上适用于旧版本的 rubyzip gem。 1.0 版更改了界面。请看:github.com/rubyzip/rubyzip#important-note write_buffer 现在是 Zip::OutputStream::write_buffer 另外,注意到新版本需要'zip/zip'不起作用。 require 'zip' 是正确的说法。【参考方案2】:Ruby 带有一个非常方便的StringIO
库 - 这可用于使用字符串作为输出 IO 对象或伪造读取由字符串支持的文件。
这里的挑战是RubyZip在创建Zip::ZipOutputStream
时不支持直接取IO对象,但是如果你看一下initialize
的实现,根据你的实验意愿,你也许可以扩展类并允许它在构造函数中使用 IO 对象或文件名。
【讨论】:
【参考方案3】:我找到了两个 RubyZip 库。
-
Chilkat's Ruby Zip Library
rubyzip on Sourceforge
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
。 ...
如果这适用于 rubyzip 实现,那么它应该是使用 ZipOutputStream
传递 ByteArrayOutputStream
的问题,这将导致它被输出到内存。
如果它在 rubyzip 中不存在,那么我相信您始终可以编写自己的实现并将其提交以包含在 rubyzip 中,因为它是开源的。
【讨论】:
我指的是 gem:rubygems.org/gems/rubyzip 我不想使用 Chilkat 库,因为它不是开源的。 我不会降级,但我没有在任何地方找到像“不,标准 Ruby ZIP 类不能这样做”这样的评论。我相信你应该从它开始。 实际上 ZipOutputStream 新方法以文件名作为参数,我没有找到任何方法来传递 IO 对象。 dimitko,没有用于 zip 的标准 ruby 库。我检查了 rubyzip,但似乎没有什么可以做我想做的事。 @flutedemetan,就像我上面说的,如果它不存在,那么你要么需要使用另一个库,要么自己添加到 rubyzip 开源项目中。【参考方案4】:如果您使用的是 Linux,并且取决于您有多少 RAM 以及您的文件有多大,您总是可以使用 tmpfs(共享内存)。然后,基于 rubyzip 磁盘的方法将起作用。 http://www.mjmwired.net/kernel/Documentation/filesystems/tmpfs.txt
【讨论】:
挂载需要超级用户权限 @AbeVoelker 是的,这个答案假定您已经有一个 tmpfs,或者有一个友好的本地系统管理员。【参考方案5】:接受的答案效果很好,但没有解决我的问题。我不想使用write_buffer method,因为它会在块关闭后自动关闭流。下面的代码 sn-p 让您可以更好地控制流的创建和关闭时间。
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
【讨论】:
以上是关于如何在不使用 Ruby 保存到磁盘的情况下生成 zip 文件?的主要内容,如果未能解决你的问题,请参考以下文章
Python 3:如何在不保存在磁盘上的情况下将 pandas 数据帧作为 csv 流上传?
如何在不写入磁盘的情况下将 AWS S3 上的文本文件导入 pandas
如何在不写入磁盘的情况下将 XML 从 Delphi 传递到 C#?
如何在不使用 PHP 将文件写入磁盘的情况下将文件发布到 REST 服务器?