Recentemente, mi è stato chiesto di aggiungere ad una applicazione Ruby on Rails una funzionalità per il download di un'intera galleria di immagini in un unico file zip.
In modo abbastanza ovvio ho deciso di sfruttare la gemma rubyzip per creare l'archivio compresso da scaricare.
Ma ciò che volevo era creare lo zip al volo, direttamente in memoria, senza popolare le cartelle della mia applicazione di file zip che avrei poi dovuto rimuovere.
Da qui l'idea di utilizzare Tempfile per creare il file in memoria.
Di seguito la funzione 'download_zip' che ho creato:
def download_zip(image_list)
if !image_list.blank?
file_name = 'pictures.zip'
t = Tempfile.new('mio-file-temp-2016-02-19 10:41:53 +0100')
Zip::ZipOutputStream.open(t.path) do |z|
image_list.each do |img|
title = img.title
title += '.jpg' unless title.end_with?('.jpg')
z.put_next_entry(title)
z.print IO.read(img.path)
end
end
send_file t.path, :type => 'application/zip', :disposition => 'attachment', :filename => file_name
t.close
end
end
Ecco come lavora la funzione download_zip:
- prende in input una lista di oggetti Image, dove Image ha le proprietà title e path relative all'immagine.
- crea il file temporaneo e lo apre come uno ZipOutputStream.
- per ogni immagine presente nella lista crea un nuovo elemento nello ZipOutputStream con nome uguale al titolo dell'immagine e contenuto letto dal path.
- successivamente, la chiusura del blocco Zip::ZipOutputStream ... end causa automaticamente la chiusura del nuovo file zip.
- a questo punto non rimane altro che inviare il file all'utente con il corretto mime type.
- infine, viene chiuso il file temporaneo, che sarà poi rimosso dalla memoria dal garbage collector.
Come vedete, con poche righe di codice si è ottenuta una soluzione molto pulita ed efficace.