Скачайте большой CSV-файл с сервера sftp в виде кусков рубина. - PullRequest
0 голосов
/ 06 июля 2018

Я хочу скачать и обработать CSV-файл, находящийся на сервере sftp, построчно. Если я использую скачать! или sftp.file.open, это буферизация целых данных в памяти, которых я хочу избежать.

Вот мой исходный код:

sftp = Net::SFTP.start(@sftp_details['server_ip'], @sftp_details['server_username'], :password => decoded_pswd)
  if sftp
    begin
      sftp.dir.foreach(@sftp_details['server_folder_path']) do |entry|
        print_memory_usage do
          print_time_spent do
            if entry.file? && entry.name.end_with?("csv")
              batch_size_cnt = 0
              sftp.file.open("#{@sftp_details['server_folder_path']}/#{entry.name}") do |file|
                header = file.gets
                header = header.force_encoding(header.encoding).encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
                csv_data = ''
                while line = file.gets
                  batch_size_cnt += 1
                  csv_data.concat(line.force_encoding(line.encoding).encode('UTF-8', invalid: :replace, undef: :replace, replace: ''))
                  if batch_size_cnt == 1000 || file.eof?
                    CSV.parse(csv_data, {headers: header, write_headers: true}) do |row|
                      row.delete(nil) 
                      entities << row.to_hash       
                    end
                    csv_data, batch_size_cnt = '', 0
                    courses.delete_if(&:blank?)
                    # DO PROCESSING PART
                    entities = []
                  end
                end if header
              end
              sftp.rename("#{@sftp_details['server_folder_path']}/#{entry.name}", "#{@sftp_details['processed_file_path']}/#{entry.name}")
            end
          end
        end
end

Может кто-нибудь помочь, пожалуйста? Спасибо

1 Ответ

0 голосов
/ 09 июля 2018

Вам нужно добавить какой-то буфер, чтобы можно было читать куски, а затем записывать их все вместе. Думаю, было бы разумно разделить ваш скрипт на разбор и загрузку. Сосредоточьтесь на одном:

Ваша оригинальная линия:

   ...
   sftp.file.open("#{@sftp_details['server_folder_path']}/#{entry.name}") do |file|
   ...

Если вы проверите исходный файл метода download! (не забывайте, взрыв!), Вы можете использовать 'stringio'. Заглушка, которую вы можете легко настроить. Обычно достаточно буфера по умолчанию, который составляет 32 КБ. Вы можете изменить его, если хотите (см. Пример).

Заменить на (работает только с отдельными файлами):

Использование StringIO:

   ...
  io = StringIO.new
  sftp.download!("#{@sftp_details['server_folder_path']}/#{entry.name}", io.puts, :read_size => 16000))

ИЛИ вы можете просто скачать файл

  ...
  file = File.open("/your_local_path/#{entry.name}",'wb')
  sftp.download!("#{@sftp_details['server_folder_path']}/#{entry.name}", file, :read_size => 16000)
  ....

В Документе вы можете использовать опцию :read_size:

: read_size - максимальное количество байтов для чтения за раз из источник. Увеличение этого значения может улучшить пропускную способность. По умолчанию 32 000 байт.

...