如何直接从 ZipEntry(RubyZip、Paperclip、Rails 3)获取临时文件对象(内容类型正确,无需写入磁盘)?

Posted

技术标签:

【中文标题】如何直接从 ZipEntry(RubyZip、Paperclip、Rails 3)获取临时文件对象(内容类型正确,无需写入磁盘)?【英文标题】:How do I get a temporary File object (of correct content-type, without writing to disk) directly from a ZipEntry (RubyZip, Paperclip, Rails 3)? 【发布时间】:2011-06-21 01:41:58 【问题描述】:

我目前正在尝试将图像文件直接从 zip 文件附加到模型(即不先将它们保存在磁盘上)。似乎应该有一种更清晰的方法将 ZipEntry 转换为可以存储在内存中的 Tempfile 或 File 以传递给知道如何处理它的另一个方法或对象。

这是我的代码:

def extract (file = nil)
  Zip::ZipFile.open(file)  |zip_file|
    zip_file.each  |image|
      photo = self.photos.build
      # photo.image = image # this doesn't work
      # photo.image = File.open image # also doesn't work
      # photo.image = File.new image.filename
      photo.save
    
  
end

但问题是 photo.image 是模型的附件(通过回形针),并且将某些内容分配为附件需要某些内容是 File 对象。但是,我一生都无法弄清楚如何将 ZipEntry 转换为文件。我见过的打开或创建文件的唯一方法是在其路径中使用字符串——这意味着我必须将文件提取到某个位置。真的,这似乎很愚蠢。为什么我不能直接将 ZipEntry 文件提取到输出流并在那里转换为 File?

所以最终的问题是:我可以从 Zip 文件中提取 ZipEntry 并将其直接转换为 File 对象(或将其直接附加为 Paperclip 对象)吗?或者我是否在附加它之前将其实际存储在硬盘上,即使该版本最终会被删除?

更新 感谢蓝莓田,我想我离我的解决方案更近了一点。这是我添加的代码行,它为我提供了我需要的 Tempfile/File:

photo.image = zip_file.get_output_stream image

但是,我的 Photo 对象不会接受通过的文件,因为它不是 image/jpeg。事实上,检查文件的content_type 显示application/x-empty。我认为这可能是因为获取输出流似乎在文件末尾附加了一个时间戳,因此它最终看起来像imagename.jpg20110203-20203-hukq0n编辑:另外,它创建的临时文件不包含任何数据,大小为 0。所以看起来这可能不是答案。

那么,下一个问题:有谁知道如何得到这个给我一个图像/jpeg 文件?

更新

我一直在玩这个。似乎输出流不是要走的路,而是输入流(这总是让我感到困惑)。在 ZipEntry 上使用 get_input_stream,我得到了文件中的二进制数据。我想现在我只需要弄清楚如何将它放入回形针附件(作为文件对象)。我尝试将 ZipInputStream 直接推送到附件,但当然,这不起作用。我真的很难相信没有人尝试将提取的 ZipEntry 转换为文件。有什么理由认为这会被认为是不好的编程习惯吗?在我看来,跳过临时文件的磁盘写入是完全可以接受的,并且在 Zip 存档管理之类的东西中得到支持。

无论如何,问题仍然存在:

有没有办法将输入流转换为文件对象(或临时文件)?最好不必写入磁盘。

【问题讨论】:

File 对象只是封装了一个文件名。它们没有任何描述的内容类型。 【参考方案1】:

试试这个

Zip::ZipFile.open(params[:avatar].path) do |zipfile|
  zipfile.each do |entry|
    filename = entry.name
    basename = File.basename(filename)

    tempfile = Tempfile.new(basename)
    tempfile.binmode
    tempfile.write entry.get_input_stream.read

    user = User.new
    user.avatar = 
      :tempfile => tempfile,
      :filename => filename
    
    user.save

  end
end

【讨论】:

这可行,但创建临时文件的语法不正确。我已经对其进行了编辑以使用不同的语法。 在本例中,您将 user.avatar 设置为哈希值。 User.avatar 是回形针::附件吗?如果是这样,你怎么能传入一个哈希?我已经尝试过了,它对我不起作用。 Paperclip:Attachment 有一组“io 适配器”,用于不同类型的对象(例如 StringIO 和 Tempfile),但我查看了 io_adapter,看不到可以处理哈希的。【参考方案2】:

查看 ZipFile 上的 get_input_stream 和 get_output_stream 消息。

【讨论】:

我试过了。使用 zip_file.get_output_stream(image) 确实产生了一个文件。但是,该模型似乎将该文件解释为“application/x-empty”,这导致它失败。它似乎还将文件名读取为“image_name.jpg20110203-20203-1lyjn3i”,我认为这是回形针为其图像提供版本跟踪的标准时间戳。你知道一种确保它被读取为 jpeg 的方法吗? 其实我刚查了一下。输出流作为临时文件打开。该时间戳是在创建该 Tempfile 时添加的。我刚刚检查了提取的 Tempfile 的 content_type,它显示为 application/x-empty。

以上是关于如何直接从 ZipEntry(RubyZip、Paperclip、Rails 3)获取临时文件对象(内容类型正确,无需写入磁盘)?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 ZipEntry 创建输入流

rubyzip 压缩文件夹时遇到Errno::EACCES (Permission denied @ rb_sysopen -

rubyZip Gem:想要在 RoR 中压缩远程文件

Java 从 ZipEntry 对象中读取指定文件

用其他gems rails调用ruby脚本

RubyZip:找不到存储在活动存储中的文件的路径