使用ruby流式处理并解压缩大型csv文件

Zom*_*Dev 8 ruby csv io rubyzip net-http

我有问题需要下载,解压缩,然后逐行处理一个非常大的CSV文件.我认为让您了解文件的大小是有用的:

  • big_file.zip~700mb
  • big_file.csv~23gb

这是我想要发生的一些事情:

  • 在解压缩之前不必下载整个文件
  • 在解析csv行之前不必解压缩整个文件
  • 在完成所有这些操作时,不要耗尽非常多的内存/磁盘

我不知道这是否可能.这就是我的想法:

require 'open-uri'
require 'rubyzip'
require 'csv'

open('http://foo.bar/big_file.zip') do |zipped|
  Zip::InputStream.open(zipped) do |unzipped|
    sleep 10 until entry = unzipped.get_next_entry && entry.name == 'big_file.csv'
    CSV.foreach(unzipped) do |row|
      # process the row, maybe write out to STDOUT or some file
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

这是我所知道的问题:

  • open-uri读取整个响应并将其保存到Tempfile这个大小的文件中.我可能需要Net::HTTP直接使用,但我不知道该怎么做,仍然得到一个IO.
  • 我不知道下载的速度有多快,或者Zip::InputStream我的工作方式是否正常.当它不是全部时,它可以解压缩一些文件吗?
  • 请问CSV.foreach用rubyzip的工作InputStream?它的行为是否足以File解析行?如果它想要读取但缓冲区是空的,它会变得怪异吗?

我不知道这是否是正确的做法.也许一些EventMachine解决方案会更好(虽然我以前从未使用过EventMachine,但如果它对这样的东西更好用,我就是为了它).

Zom*_*Dev 11

自从我发布这个问题已经有一段时间了,如果有人遇到它,我认为可能值得分享我发现的东西.

  1. 对于我处理Ruby标准库的行数CSV太慢了.我的csv文件很简单,无论如何我都不需要处理引用的字符串或类型强制.只使用它IO#gets然后在逗号上拆分行就容易得多.
  2. 我无法将整个内容从http流式传输Zip::InputstreamIO包含csv数据的内容.这是因为zip文件结构文件末尾具有中央目录结束(EOCD).这是为了提取文件所需要的,因此从http流式传输它似乎不会起作用.

我最终得到的解决方案是将文件下载到磁盘,然后使用Ruby的open3库和Linux unzip软件包从zip中流式传输未压缩的csv文件.

require 'open3'

IO.popen('unzip -p /path/to/big_file.zip big_file.csv', 'rb') do |io|
  line = io.gets
  # do stuff to process the CSV line
end
Run Code Online (Sandbox Code Playgroud)

-p上解压开关所抽取的文件发送到stdout.IO.popen然后使用管道将其作为IO红宝石中的对象.工作得很好.你也可以使用它,CSV如果你想要额外的处理,那对我来说太慢了.

require 'open3'
require 'csv'

IO.popen('unzip -p /path/to/big_file.zip big_file.csv', 'rb') do |io|
  CSV.foreach(io) do |row|
    # process the row
  end
end
Run Code Online (Sandbox Code Playgroud)