Creare un file zip al volo con ruby

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.