【问题标题】:How to iterate through an in-memory zip file in Ruby如何在 Ruby 中遍历内存中的 zip 文件
【发布时间】:2012-11-23 17:18:00
【问题描述】:

我正在编写一个单元测试,其中一个正在返回一个 zip 文件,我想检查这个 zip 文件的内容,从中获取一些值,然后将这些值传递给下一个测试。

我正在使用 Rack Test,所以我知道我的 zip 文件的内容在 last_response.body 中。我查看了RubyZip 的文档,但似乎它总是期待一个文件。由于我正在运行单元测试,因此我更喜欢在内存中完成所有操作,以免测试 zip 文件污染任何文件夹(如果可能)。

【问题讨论】:

    标签: ruby zip in-memory rack-test


    【解决方案1】:

    马特的回答完全正确。这里更新为新的 API:

    Zip::InputStream.open(StringIO.new(input)) do |io|
      while entry = io.get_next_entry
        if entry.name == 'doc.kml'
          parse_kml(io.read)
        else
          raise "unknown entry in kmz file: #{entry.name}"
        end
      end
    end
    

    并且不再需要对 StringIO 进行monkeypatch。进步!

    【讨论】:

    • 输入需要是什么?文件?路径?
    【解决方案2】:

    请参阅@bronson’s answer 以获取使用更新的 RubyZip API 的此答案的更新版本。

    您链接到的 Rubyzip 文档看起来有点旧。 latest release (0.9.9) can handle IO objects,因此您可以使用 StringIO(稍作调整)。

    即使 api 将接受 IO,它似乎仍然假定它是一个文件并尝试在其上调用 path,所以首先要添加一个 StringIO 的猴子补丁 path 方法(它没有不需要实际做任何事情):

    require 'stringio'
    class StringIO
      def path
      end
    end
    

    然后你可以这样做:

    require 'zip/zip'
    Zip::ZipInputStream.open_buffer(StringIO.new(last_response.body)) do |io|
      while (entry = io.get_next_entry)
        # deal with your zip contents here, e.g.
        puts "Contents of #{entry.name}: '#{io.read}'"
      end
    end
    

    一切都将在内存中完成。

    【讨论】:

      【解决方案3】:
      Zip::File.open_buffer(content) do |zip|
        zip.each do |entry|
          decompressed_data += entry.get_input_stream.read
        end
      end
      

      【讨论】:

      • 一些解释可以大大促进理解,而不是复制粘贴继续
      【解决方案4】:

      使用RubyZip 版本1.2.1(或者可能还有一些以前的版本),我们只需要使用Zip::File 类的open_buffer 方法。

      来自 RubyZip 文档:

      类似于#open,但从字符串或打开的 IO 流中读取 zip 存档内容,并将数据输出到缓冲区。 (这可用于从下载的 zip 存档中提取数据,而无需先将其保存到磁盘。)

      例子:

      Zip::File.open_buffer(last_response.body) do |zip|
        zip.each do |entry|
          puts entry.name
          # Do whatever you want with the content files.
        end
      end
      

      【讨论】:

      • 这对你有用吗?当我这样做时,我得到了详细的错误here
      • 适合我,推荐这个
      【解决方案5】:

      您可以使用Tempfile 将 zip 文件转储到临时文件中。 Tempfile 创建一个特定于操作系统的临时文件,该文件将在您的程序完成后由操作系统清理。

      【讨论】:

      • 在 POSIX 系统上,临时文件在您获取时已被“删除”,因此无需清理。这是最接近临时文件对象的裸文件句柄的方法。
      【解决方案6】:

      受马特回答的启发,我为必须使用 0.9.x ruby​​zip gem 的人提供了一个稍微修改的解决方案。我的不需要新的类定义。

      sio = StringIO.new(response.body)
      sio.define_singleton_method(:path) {} #needed to create fake method path TO satisfy the ancient rubyzip 0.9.8 gem
      Zip::ZipInputStream::open_buffer(sio) { |io|
          while (entry = io.get_next_entry)
              puts "Contents of #{entry.name}"
           end
      }
      

      【讨论】:

        【解决方案7】:

        这对我有用。就我而言,我只有一个文件,所以我使用了固定路径,但您可以使用 entry.name 来构建您的路径。

        input = HTTParty.get(link).body
        Zip::File.open_buffer(input) do |zip_file|
            zip_file.each do |entry|
              entry.extract(path)
            end
        end
        

        【讨论】:

          【解决方案8】:

          由于 ruby​​zip 的更改,仅对此进行了更新:

          Zip::InputStream.open(StringIO.new(zip_file)) do |io|
            while (entry = io.get_next_entry)
              # deal with your zip contents here, e.g.
              puts "Contents of #{entry.name}: '#{io.read}'"
            end
          end
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-12-18
            • 1970-01-01
            • 1970-01-01
            • 2012-06-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多