> 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
这是我所知道的问题:
> open-uri读取整个响应并将其保存到Tempfile中,这对于这个大小的文件来说是不合适的.我可能需要直接使用Net :: HTTP,但我不知道如何做到这一点仍然得到一个IO.
>我不知道下载的速度有多快,或者Zip :: InputStream是否按照我显示的方式工作.当它不是全部时,它可以解压缩一些文件吗?
> CSV.foreach可以使用rubyzip的InputStream吗?它是否足够像File一样可以解析行?如果它想要读取但缓冲区是空的,它会变得怪异吗?
我不知道这是否是正确的做法.也许一些EventMachine解决方案会更好(虽然我以前从未使用过EventMachine,但如果它对这样的东西更好用,我就是为了它).
解决方法
>对于我处理Ruby标准库CSV的行数太慢了.我的csv文件很简单,无论如何我都不需要处理引用的字符串或类型强制.只需使用IO#gets然后在逗号上拆分行就容易多了.
>我无法将整个事物从http流式传输到Zip :: Inputstream到包含csv数据的IO.这是因为zip file structure在文件末尾有中央目录结束(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
解压缩时-p开关将提取的文件发送到stdout. IO.popen然后使用管道在ruby中创建一个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