Ruby-как получить имя файла с open-uri?

Я хочу загрузить музыкальный файл следующим образом:

require 'open-uri'

source_url = "http://soundcloud.com/stereo-foo/cohete-amigo/download"

attachment_file = "test.wav"

open(attachment_file, "wb") do |file|  
  file.print open(source_url).read
end

в этом примере я хочу изменить " Test.wav " для реального имени файла (например, программа JDownloader).

редактировать: Я не имею в виду временной файл, я имею в виду сохраненный файл в интернете, как Jdownloader получает: "Cohete Amigo - Stereo Foo.WAV и"

Спасибо, что прочитала

обновление:

Я пробовал это, чтобы сохранить имя:

attachment_file = File.basename(open(source_url))

I думаю, в этом нет смысла, но я не знаю, как это сделать, извините.

2 ответов


имя файла хранится в поле с именем Content-Disposition. Однако декодирование этого поля может быть немного сложным. См. некоторые обсуждения здесь, например:

как кодировать параметр filename заголовка Content-Disposition в HTTP?

на open-uri вы можете получить доступ ко всем полям заголовка через meta доступа возвращенных File класс:

f = open('http://soundcloud.com/stereo-foo/cohete-amigo/download')
f.meta['content-disposition']
=> "attachment;filename=\"Stereo Foo - Cohete Amigo.wav\""

Итак, чтобы расшифровать что-то подобное, вы мог бы это сделать:

cd = f.meta['content-disposition'].
filename = cd.match(/filename=(\"?)(.+)/)[2]
=> "Stereo Foo - Cohete Amigo.wav"

он работает для вашего конкретного случая, и он также работает, если кавычки " нет. Но в более сложных случаях размещения контента, таких как имена файлов UTF-8, вы можете попасть в небольшую неприятность. Не уверен, как часто UTF-8 используется, хотя, и если даже soundcloud когда-либо использует UTF-8. Так что, возможно, Вам не нужно беспокоиться об этом (не подтверждено и не проверено).

вы также можете использовать более продвинутую структуру веб-обхода, такую как Mechanize, и доверьте ему сделать расшифровывать для вас:

require 'mechanize'

agent = Mechanize.new
file = agent.get('http://soundcloud.com/stereo-foo/cohete-amigo/download')
file.filename
=> "Stereo_Foo_-_Cohete_Amigo.wav"

File.basename(open(source_url)) не будет работать, потому что open(source_url) возвращает дескриптор ввода-вывода, а не строку типа File.basename ожидает.

File.basename(source_url)

было бы лучше работать, если URL-адрес не использует некоторые path/to/service/with/parameters/in/line/like/this тип кодировки.

библиотека URI Ruby имеет полезные инструменты, чтобы помочь здесь. Что-то вроде:

File.basename(URI.parse(source_url).path)

будет отправной точкой. Например:

require 'uri'

File.basename(URI.parse('http://www.example.com/path/to/file/index.html').path
# => "index.html"

и:

File.basename(URI.parse('http://www.example.com/path/to/file/index.html?foo=bar').path)
# => "index.html"

вы знаю, если я могу извлечь файл и как?

отличный способ проверить http-материал локально-запустить gem server из командной строки, и пусть драгоценные камни запускают небольшой веб-сервер для его документации:

require 'open-uri'

html_doc = open('http://0.0.0.0:8808/') do |io|
  puts io.size
  io.read
end

puts html_doc.size

# => 114350
# => 114350

когда вы используете блок с OpenURI в open команда, она дает вам доступ к большому количеству информации о соединении в переменной блока, которая является экземпляром Tempfile класса. Так, вы можете узнать размер входящего файла, используя size.

это нормально для небольших файлов, но если вы вытаскиваете большой файл, который вы можете исследовать с помощью Net:: HTTP для отправки head запрос, который может включить размер. Я говорю может, потому что иногда сервер не знает, сколько будет возвращено, в случае динамического контента или контента, возвращаемого CGI или субсервисом, который не потрудился сказать.

преимущество использования запроса "head" заключается в том, что сервер не возвращает весь контент, а только заголовки. Итак, в прошлом я предварял запрос, используя head, чтобы посмотреть, смогу ли я получить необходимые мне данные. Если нет, я был бы вынужден вытащить полный ответ, используя нормальный get.