Загрузите большой CSV-файл с sftp-сервера кусками ruby

Я хочу загрузить и обработать 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

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


person Kiran Kumawat    schedule 06.07.2018    source источник
comment
Добро пожаловать в SO. Прежде чем мы сможем вам помочь, мы поощряем некоторые усилия. Что ты пробовал?   -  person tukan    schedule 06.07.2018
comment
@tukan Я пробовал sftp.file.open этот буфер по 32 Кбайт за раз и создавал пакеты по 1000 записей для их сохранения.   -  person Kiran Kumawat    schedule 06.07.2018
comment
Не могли бы вы обновить свой вопрос фактическим кодом? См. - stackoverflow.com/help/mcve для получения дополнительной информации.   -  person tukan    schedule 06.07.2018
comment
полезно: stackoverflow.com/questions/2538613/   -  person Sergio Tulentsev    schedule 09.07.2018


Ответы (1)


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

Ваша исходная строка:

   ...
   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 байт.

person tukan    schedule 09.07.2018