Net::SFTP Ошибки

Я пытался загрузить файл, используя Net::SFTP, и он продолжает получать сообщение об ошибке.

файл частично загружен, и его размер составляет всего 2,1 МБ, поэтому это не очень большой файл. Я удалил цикл над файлами и даже попытался просто загрузить один файл и получил ту же ошибку:

yml = YAML.load_file Rails.root.join('config', 'ftp.yml')
Net::SFTP.start(yml["url"], yml["username"], password: yml["password"]) do |sftp|
  sftp.dir.glob(File.join('users', 'import'), '*.csv').each do |f|
    sftp.download!(File.join('users', 'import', f.name), Rails.root.join('processing_files', 'download_files', f.name), read_size: 1024)
  end
end

NoMethodError: неопределенный метод close' for #<Pathname:0x007fc8fdb50ea0> from /[my_working_ap_dir]/gems/net-sftp-2.1.2/lib/net/sftp/operations/download.rb:331:inon_read"

Любая помощь будет оценена. Я помолился, чтобы погуглить все, что я могу, и не получаю нигде с этим.

2 ответа

Решение

Rails.root возвращает объект Pathname, но похоже, что код sftp не проверяет, получил ли он Pathname или дескриптор файла, он просто работает с ним. Когда он сталкивается с entry.sink.close происходит сбой, потому что Pathnames не реализуют close.

Имена путей отлично подходят для манипулирования путями к файлам и каталогам, но они не заменяют файловые дескрипторы. Вы могли бы, вероятно, придерживаться to_s который бы возвратил строку.

Вот краткое изложение download вызов из документации, которая намекает на то, что ожидаемые параметры должны быть String:

Чтобы загрузить один файл с удаленного сервера, просто укажите оба
удаленные и локальные пути:

  downloader = sftp.download ("/ path / to / remote.txt", "/path/to/local.txt")

Я подозреваю, что если я покопаюсь в коде, он проверит, являются ли параметры строками, и, если нет, предполагает, что они являются дескрипторами ввода-вывода.

Увидеть ri Net::SFTP::Operations::Download для получения дополнительной информации.


Вот выдержка из текущего download! код, и вы можете увидеть, как возникла проблема:

def download!(remote, local=nil, options={}, &block)
  require 'stringio' unless defined?(StringIO)
  destination = local || StringIO.new
  result = download(remote, destination, options, &block).wait
  local ? result : destination.string
end

local был передан в качестве пути. Код проверяет, есть ли что-то переданное, но не то, что это. Если ничего не передается, предполагается, что это что-то с IO-подобными функциями, что StringIO обеспечивает для кэширования в памяти.

Видимо, вы не можете использовать Rails.root.join, который был причиной проблемы. Это действительно глупо, потому что он загружает часть файла.

Изменен:

sftp.download!(File.join('users', 'import', f.name), Rails.root.join('processing_files', 'download_files', f.name))

Для того, чтобы:

sftp.download!(File.join('users', 'import', f.name), File.join('processing_files', 'download_files', f.name))

аргумент remote может быть Pathname объект пока аргумент local при установке должен быть String или же объект, который реагирует на #writeметод. Ниже рабочий код

local_stringified_path = Rails.root.join('processing_files', f.name).to_s
sftp.download!(Pathname.new('/users/import'), local_stringified_path)

Всем любопытным, пожалуйста, прочтите ниже, чтобы понять это поведение.

Проблема NoMethodError: undefined method close' for #<Pathname:0x007fc8fdb50ea0>происходит именно здесь, в#on_read метод, а ниже - фрагмент кода соответствующих операторов.

if response.eof? 
   update_progress(:close, entry)
   entry.sink.close # ERRORED OUT LINE.. ideally when eof, file IO handler is supposed to be closed

ЧТО ТАКОЕ entry.sink?

Мы уже знаем, что #download! методы принимают два аргумента, как показано ниже

sftp.download!(remote, local)

Данные аргументы remote а также localпреобразуется в объект входа здесь

[Entry.new(remote, local, recursive?)]

а также Entry это не что иное, как Struct Вот

Entry = Struct.new(:remote, :local, :directory, :size, :handle, :offset, :sink)

хорошо, тогда что sinkатрибут? мы сразу перейдем к этому..

Как только соответствующий удаленный файл открыт для чтения, #on_open метод обновляет это sinkатрибут с обработчиком файла здесь.

Найдите фрагмент ниже,

entry.sink = entry.local.respond_to?(:write) ? entry.local : ::File.open(entry.local, "wb")

На самом деле это происходит только когда дано local объект пути не реализует свой собственный #writeметод В нашем сценарииPathnameобъекты реагируют на запись

Ниже приведены некоторые фрагменты выходных данных консоли, которые я проверил между вызовами нескольких фрагментов загрузки во время отладки.. который показывает entry а также entry.sinkотображение обсуждаемых выше объектов. Здесь я выбрал свой пульт в качествеPathname объект и локальный быть String путь, который возвращает правильное значение для entry.sink и там, скачав успешно..

0> entry 
=> #<struct Net::SFTP::Operations::Download::Entry remote=#<Pathname:214010463.xml>, local="214010463.xml", directory=nil, size=nil, handle="1", offset=32000, sink=#<File:214010463.xml>>


0> entry.sink
=> #<File:214010463.xml>
Другие вопросы по тегам