如何仅在内存中修改zip文件?

时间:2010-12-03 22:43:24

标签: ruby-on-rails ruby zip

我有一个Ruby应用程序,我需要修改现有的zip文件。

我想在内存中构建zip文件并将字节流回传而无需将文件写入文件系统。如果我最终在Heroku上托管这个,我不认为我可以写入文件系统。有谁知道这样做的方法?

我看了Zip::ZipFile,但看起来总是想要写入文件系统。我认为“基于java实现”我可以获得压缩文件的字节,你可以在java中做,但我没有办法做到这一点。


编辑:

我所要求的基本上与此相同,但对于Ruby而不是Python: Function to create in-memory zip file and return as http response

4 个答案:

答案 0 :(得分:4)

有同样的问题,通过关闭文件并读取数据并将其作为send_data传输来实现它的工作

然后找到另一个在heroku上工作正常的库,可以处理内存缓冲区:它是zipruby(不是rubyzip)。

buffer = ''
Zip::Archive.open_buffer(buffer, Zip::CREATE) do |archive|
  files.each do |wood, report|
    title = wood.abbreviation+".txt"
    archive.add_buffer(title, report);
  end
end
file_name = "dimter_#{@offer.customerName}_#{Time.now.strftime("%m%d%Y_%H%M")}.zip"
send_data buffer, :type => 'application/zip', :disposition => 'attachment', :filename => file_name

答案 1 :(得分:3)

这是一个处理此问题的blog post。它使用Tempfile,对我来说似乎是一个很好的解决方案(尽管通过评论阅读了一些有用的额外讨论)。

一个例子,来自帖子:

def download_zip(image_list)
  if !image_list.blank?
    file_name = "pictures.zip"
    t = Tempfile.new("my-temp-filename-#{Time.now}")
    Zip::ZipOutputStream.open(t.path) do |z|
      image_list.each do |img|
        title = img.title
        title += ".jpg" unless title.end_with?(".jpg")
        z.put_next_entry(title)
        z.print IO.read(img.path)
      end
    end
    send_file t.path, :type => 'application/zip',
                      :disposition => 'attachment',
                      :filename => file_name
    t.close
  end
end

此解决方案should play nice with Heroku

答案 2 :(得分:1)

您可以随时修补Zip :: ZipFile的newopen方法以允许使用StringIO句柄,然后将I / O直接写入内存。

答案 3 :(得分:1)

在这里提出我自己的问题的答案,我认为更适合我想要做的事情。这个方法实际上没有文件(没有临时文件)。

由于ZipFile扩展,并且实际上只是ZipCentralDirectory的一堆便利方法,您可以直接使用ZipCentralDirectory而不是ZipFile。这将使您使用IO流来创建和编写zip文件。再加上使用StringIO,您可以从字符串中执行此操作:

  # load a zip file from a URL into a string
  resp = Net::HTTP.new("www.somewhere.com", 80).get("/some.zip")
  zip_as_string = response.body

  # open as a zip
  zip = Zip::ZipCentralDirectory.read_from_stream(StringIO.new(zip_as_string))

  # work with the zip file.
  # i just output the names of each entry to show that it was read correctly
  zip.each { |zf| puts zf.name }

  # write zip back to an output stream
  out = StringIO.new
  zip.write_to_stream(out)

  # use 'out' or 'out.string' to do whatever with the resulting zip file.
  out.string

更新

这实际上根本不起作用。它会写一个可读的zip文件,但只有zip文件的'目录'。所有内部文件都是0长度。进一步深入Zip实现,看起来它只在内存中保存zip条目'元数据',并返回到底层文件以读取其他所有内容。基于此,看起来如果不写文件系统就完全不可能使用Zip实现。