2012-11-15 4 views
8

Vorrei scaricare un file musicale in questo modo:Ruby - Come ottenere il nome di un file con 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 

In questo esempio voglio cambiare "Test.wav" al nome del file reale (come ad esempio il programma JDownloader fa).

EDIT: non intendo il file temporale, voglio dire, il file memorizzato nel web come Jdownloader ottiene: "Cohete Amigo - foo.wav Stereo"

Grazie per leggere

UPDATE :

ho provato questo per memorizzare il nome:

attachment_file = File.basename(open(source_url)) 

penso che non ha senso, ma non so la strada per farlo, mi dispiace.

risposta

15

Il nome file è memorizzato nel campo dell'intestazione denominato Content-Disposition. Tuttavia la decodifica di questo campo può essere un po 'complicata. Vedi qualche discussione qui per esempio:

How to encode the filename parameter of Content-Disposition header in HTTP?

Per open-uri è possibile accedere a tutti i campi di intestazione attraverso la meta di accesso del restituita File classe:

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

Quindi, al fine di decodificare una cosa del genere puoi fare questo:

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

Funziona per il tuo particolare caso, e funziona anche se le virgolette " non sono presenti. Ma nei casi più complessi di disposizione dei contenuti, come i nomi di file UTF-8, potresti avere un piccolo problema. Non sono sicuro di quanto spesso UTF-8 sia usato, e se anche soundcloud utilizza mai UTF-8. Quindi forse non devi preoccuparti di ciò (non confermato né testato).

Si potrebbe anche usare un framework web-crawling più avanzate come Mechanize, e la fiducia per fare la decodifica per voi:

require 'mechanize' 

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

grazie, sai se posso recuperare anche il file e senza aspettare di ottenere tutto il file mb del file? – ElektroStudios

+0

Guarda all'interno dell'intestazione Content-length. – Danyel

6

File.basename(open(source_url)) non funzionerà perché open(source_url) restituisce un handle di I/O di qualche ordina, non una stringa come File.basename si aspetta.

File.basename(source_url) 

avrebbe una migliore possibilità di lavoro, a meno che l'URL sta usando qualche codifica path/to/service/with/parameters/in/line/like/this tipo.

La libreria URI di Ruby ha comunque utili strumenti per aiutare. Qualcosa di simile:

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

sarebbe un punto di partenza.Per esempio:

require 'uri' 

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

e:

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

sapete se posso prelevare la dimensione del file troppo e come?

Un ottimo modo per testare roba HTTP a livello locale, è quello di eseguire gem server dalla riga di comando, e lasciare che le gemme fuoco su un piccolo server web per la sua documentazione:

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 

Quando si utilizza un blocco con il comando open di OpenURI, ti dà accesso a molte informazioni sulla connessione nella variabile di blocco, che è un'istanza della classe Tempfile. Quindi, puoi scoprire la dimensione del file in arrivo usando size.

che è ok per i file di piccole dimensioni, ma se si sta tirando in un file di grandi dimensioni si potrebbe desiderare di indagare con Net :: HTTP per inviare una richiesta di head, che potrebbe includere la dimensione. Dico potrebbe, perché a volte il server non sa quanto verrà restituito, nel caso di contenuto dinamico, o il contenuto viene restituito da un CGI o sottoservizio che non si preoccupa di dire.

Il vantaggio di utilizzare una richiesta "testa" è che il server non restituisce l'intero contenuto, solo le intestazioni. Quindi, in passato, ho preimpostato una richiesta usando head, per vedere se potevo ottenere i dati di cui avevo bisogno. In caso contrario, sarei costretto a inserire la risposta completa utilizzando un normale get.

+0

grazie, sai se posso recuperare anche il file e come? – ElektroStudios

+0

Il file è più difficile. Viene spesso restituito nelle intestazioni HTTP restituite dal server e accessibile tramite i metodi Net :: HTTP. Alcuni di essi sono disponibili nelle intestazioni di OpenURI se si utilizza un blocco con 'open'. Il problema è che non tutte le richieste producono un'intestazione Content-Length perché il server non sa quanto sta tornando. Questo è particolarmente vero per il contenuto dinamico generato da un CGI di qualche tipo. –

+0

grazie per le informazioni – ElektroStudios

Problemi correlati