我有问题需要下载,解压缩,然后逐行处理一个非常大的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,但是如果它对这样的东西效果更好,我就是为了它)。
答案 0 :(得分:10)
自从我发布此问题以来已经有一段时间了,万一其他人遇到它我认为可能值得分享我找到的内容。
CSV
的行数太慢了。我的csv文件非常简单,无论如何我都不需要所有的东西来处理引用的字符串或类型强制。只需使用IO#gets
然后在逗号上拆分行即可。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