2010-02-26 11 views
15

Paperclip es un excelente complemento de carga para Rails. El almacenamiento de las cargas en el sistema de archivos local o Amazon S3 parece funcionar bien. Simplemente asumiría que los archivos de la tienda en el localhost, pero el uso de S3 es necesario para esta aplicación, ya que se alojará en Heroku.Comprima todos los archivos adjuntos de Paperclip almacenados en S3

¿Cómo podría obtener todas mis cargas/archivos adjuntos de S3 en una sola descarga comprimida?

Obtener un archivo comprimido de archivos desde el sistema de archivos local parece sencillo. Es obtener los archivos de S3 que me tiene perplejo. Creo que puede tener algo que ver con la forma en que rubyzip maneja los archivos referenciados por URL. He intentado varios enfoques, pero parece que no puedo evitar errores.

format.zip { 
       registrations_with_attachments = Registration.find_by_sql('SELECT * FROM registrations WHERE abstract_file_name NOT LIKE ""') 
       headers['Cache-Control'] = 'no-cache' 
       tmp_filename = "#{RAILS_ROOT}/tmp/tmp_zip_" << 
           Time.now.to_f.to_s << 
           ".zip" 

       # rubyzip gem version 0.9.1 
       # rdoc http://rubyzip.sourceforge.net/     
       Zip::ZipFile.open(tmp_filename, Zip::ZipFile::CREATE) do |zip| 
        #get all of the attachments 

        # attempt to get files stored on S3 
        # FAIL 
        registrations_with_attachments.each { |e| zip.add("abstracts/#{e.abstract.original_filename}", e.abstract.url(:original, false)) } 
        # => No such file or directory - http://s3.amazonaws.com/bucket/original/abstract.txt 
        # Should note that these files in S3 bucket are publicly accessible. No ACL. 

        # works with local storage. Thanks to Henrik Nyh 
        # registrations_with_attachments.each { |e| zip.add("abstracts/#{e.abstract.original_filename}", e.abstract.path(:original)) } 
       end  

       send_data(File.open(tmp_filename, "rb+").read, :type => 'application/zip', :disposition => 'attachment', :filename => tmp_filename.to_s) 
       File.delete tmp_filename 
      } 
+0

Se pregunta si la solución es usar la gema AWS-S3 para obtener todos los archivos en el cubo y no usar clip? – chaserx

+0

Es casi seguro que desea usar to_file() en lugar de url(). – vladr

+0

Sí. Yo también pensé en eso. Da este error no se puede convertir Paperclip :: Tempfile en String – chaserx

Respuesta

11

Es casi seguro que desea utilizar en lugar de e.abstract.to_file.pathe.abstract.url(...).

Ver:

ACTUALIZACIÓN

Desde el changelog:

Nuevo en 3.0.1:

+0

Gracias, Vlad. Parece que funciona ahora. – chaserx

+0

actualiza esta respuesta a paperclip> 3.0, .to_file ha sido eliminado :( –

+0

@DavidMauricio Actualicé la respuesta – vladr

2

@ solución de vlard está bien. Sin embargo, he encontrado algunos problemas con el to_file. Crea un archivo temporal y el recolector de elementos no utilizados elimina (a veces) el archivo antes de agregarlo al archivo zip. Por lo tanto, recibo errores aleatorios Errno::ENOENT: No such file or directory.

Así que estoy usando el siguiente código ahora (me he mantenido los nombres de las variables iniciales de código para mantener la coherencia con la pregunta inicial)

format.zip { 
      registrations_with_attachments = Registration.find_by_sql('SELECT * FROM registrations WHERE abstract_file_name NOT LIKE ""') 
      headers['Cache-Control'] = 'no-cache' 

      #please note that using nanoseconds option in strftime reduces the risks concerning the situation where 2 or more users initiate the download in the same time 
      tmp_filename = "#{RAILS_ROOT}/tmp/tmp_zip_" << 
          Time.now.strftime('%Y-%m-%d-%H%M%S-%N').to_s << 
          ".zip" 

      # rubyzip gem version 0.9.4     
      zip = Zip::ZipFile.open(tmp_filename, Zip::ZipFile::CREATE) 
      zip.close 

      registrations_with_attachments.each { |e| 
       file_to_add = e.file.to_file 
       zip = Zip::ZipFile.open(tmp_filename) 
       zip.add("abstracts/#{e.abstract.original_filename}", file_to_add.path) 
       zip.close 
       puts "added #{file_to_add.path} to #{tmp_filename}" #force garbage collector to keep the file_to_add until after the file has been added to zip 
      } 

      send_data(File.open(tmp_filename, "rb+").read, :type => 'application/zip', :disposition => 'attachment', :filename => tmp_filename.to_s) 
      File.delete tmp_filename 
     } 
+0

¿Puede explicar por qué su solución obliga al recolector de basura a esperar? ¡Gracias! – Jared

+1

No puedo explicar por qué funciona, pero puedo confirmar que estaba teniendo el mismo problema y esto lo solucionó. – Oll

+0

¿Qué es 'b +' en el método File.open? Creo que solo hay 'r (+)', 'w (+)' y 'a (+)'. – Marc

Cuestiones relacionadas