2014-04-29 16 views
6

Tôi gặp sự cố khi cần tải xuống, giải nén và xử lý từng dòng một tệp CSV rất lớn. Tôi nghĩ rằng nó rất hữu ích để cung cấp cho bạn một ý tưởng như thế nào lớn các tập tin là:Phát và giải nén tệp csv lớn bằng ruby ​​

  • big_file.zip ~ 700mb
  • big_file.csv ~ dung lượng 23 GB

Dưới đây là một số những điều tôi muốn xảy ra:

  • Đừng có để tải về toàn bộ tập tin trước khi giải nén
  • Đừng có để giải nén toàn bộ tập tin trước khi phân tích dòng csv
  • 0.123.
  • Không sử dụng hết bộ nhớ/đĩa trong khi thực hiện tất cả điều này

Tôi không biết điều đó có thể xảy ra hay không. Dưới đây là những gì tôi đã suy nghĩ:

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 

Dưới đây là những vấn đề tôi biết về:

  • open-uri đọc toàn bộ phản ứng và lưu nó vào một Tempfile đó là không tốt với một tập tin kích thước này. Tôi có thể cần phải sử dụng trực tiếp Net::HTTP nhưng tôi không chắc chắn làm thế nào để làm điều đó và vẫn nhận được một IO.
  • Tôi không biết tốc độ tải xuống sẽ như thế nào hoặc nếu Zip::InputStream hoạt động theo cách tôi đã hiển thị nó đang hoạt động. Nó có thể giải nén một số tập tin khi nó không phải tất cả chưa?
  • Liệu số CSV.foreach có hoạt động với số InputStream của rubyzip không? Nó có hoạt động đủ như File rằng nó có thể phân tích các hàng không? Nó sẽ freak ra nếu nó muốn đọc nhưng bộ đệm trống?

Tôi không biết liệu đây có phải là phương pháp phù hợp hay không. Có lẽ một số giải pháp EventMachine sẽ tốt hơn (mặc dù tôi chưa bao giờ sử dụng EventMachine trước đây, nhưng nếu nó hoạt động tốt hơn cho một cái gì đó như thế này, tôi là tất cả cho nó).

+0

Tôi không nghĩ việc phát trực tuyến zip sẽ hoạt động do cách tệp zip được cấu trúc. Nó có thể có thể làm một cái gì đó như 'funzip' nếu chỉ có một tập tin trong zip (hoặc một trong những tôi muốn là lần đầu tiên) nhưng đó không phải là trường hợp. – ZombieDev

Trả lời

6

Đã một thời gian kể từ khi tôi đăng câu hỏi này và trong trường hợp bất kỳ ai khác bắt gặp nó, tôi nghĩ có thể đáng chia sẻ những gì tôi đã tìm thấy.

  1. Đối với số hàng tôi đang xử lý với thư viện chuẩn của Ruby CSV quá chậm. Tệp csv của tôi đủ đơn giản nên tôi không cần tất cả những thứ đó để đối phó với các chuỗi được trích dẫn hoặc loại ép buộc. Nó dễ dàng hơn nhiều chỉ sử dụng IO#gets và sau đó chia dòng trên dấu phẩy.
  2. Tôi không thể truyền toàn bộ nội dung từ http đến Zip::Inputstream tới một số IO chứa dữ liệu csv. Điều này là do zip file structure có End of Central Directory (EOCD) ở cuối tệp. Đó là cần thiết để trích xuất các tập tin để streaming nó từ http có vẻ như nó sẽ làm việc.

Giải pháp mà tôi đã kết thúc là tải tệp xuống đĩa rồi sử dụng thư viện open3 của Ruby và gói Linux unzip để truyền tệp csv chưa nén từ mã zip.

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 

Các -p bật unzip gửi file trích xuất vào stdout. IO.popen sau đó sử dụng các đường ống để làm cho đối tượng IO trong ruby. Hoạt động khá tốt. Bạn cũng có thể sử dụng nó với CSV nếu bạn muốn xử lý thêm, nó quá chậm đối với tôi.

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 
Các vấn đề liên quan