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:in
on_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>