如何在不使用 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】:

我有一个类似的问题,我使用 ruby​​zip gem 和 stringio 对象解决了这个问题。 原来,rubyzip 提供了一个返回 stringio 对象的方法:ZipOutputStream.write_buffer

您可以使用put_next_entry 创建您喜欢的 zip 文件结构并写入,完成后您可以倒回字符串并使用 sysread 读取二进制数据。

请参阅以下简单示例(适用于 ruby​​zip 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] 上测试)

以下示例适用于 ruby​​zip >= 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] 和 ruby​​zip gem 1.1.7 上测试

【讨论】:

谢谢,它有效。但是在我写这个问题的时候,这个方法还不存在。它是在 2011-01-07 中添加的。感谢您的回答,下次我需要压缩内存时,我会知道如何。 LoadError 无法加载此类文件 -- zip 以上适用于旧版本的 ruby​​zip 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 ruby​​zip 基于java.util.zip,这导致它有一个名为ZipOutputStream 的类。我不知道 ruby​​zip 实现有多好,但是使用java.util.zip 实现,OutputStream 可以设置为ByteArrayOutputStreamFileOutputStreamFilterOutputStreamObjectOutputStreamOutputStreamPipedOutputStream。 ...

如果这适用于 ruby​​zip 实现,那么它应该是使用 ZipOutputStream 传递 ByteArrayOutputStream 的问题,这将导致它被输出到内存。

如果它在 ruby​​zip 中不存在,那么我相信您始终可以编写自己的实现并将其提交以包含在 ruby​​zip 中,因为它是开源的。

【讨论】:

我指的是 gem:rubygems.org/gems/rubyzip 我不想使用 Chilkat 库,因为它不是开源的。 我不会降级,但我没有在任何地方找到像“不,标准 Ruby ZIP 类不能这样做”这样的评论。我相信你应该从它开始。 实际上 ZipOutputStream 新方法以文件名作为参数,我没有找到任何方法来传递 IO 对象。 dimitko,没有用于 zip 的标准 ruby​​ 库。我检查了 ruby​​zip,但似乎没有什么可以做我想做的事。 @flutedemetan,就像我上面说的,如果它不存在,那么你要么需要使用另一个库,要么自己添加到 ruby​​zip 开源项目中。【参考方案4】:

如果您使用的是 Linux,并且取决于您有多少 RAM 以及您的文件有多大,您总是可以使用 tmpfs(共享内存)。然后,基于 ruby​​zip 磁盘的方法将起作用。 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 服务器?

在不使用 iostream 的情况下保存 c++11 随机生成器的状态

如何在不将单独的帧图像写入磁盘的情况下从 C++ 程序中生成的多个图像编码视频?