2012-03-28 13 views
19

tengo una pequeña Rails 3.2.1 aplicación que utiliza CarrierWave 0.5.8 para la carga de archivos a S3 (FOG) utilizandoarchivos de comprimir Descarga y que fueron subidos a S3 con CarrierWave

Quiero que los usuarios puedan seleccionar algunas imágenes que les gustaría descargar, luego comprimirlas y enviarles un zip. Esto es lo que he llegado con:

def generate_zip 
    #A collection of Photo objects. The Photo object has a PhotoUploader mounted. 
    photos = Photo.all 

    tmp_filename = "#{Rails.root}/tmp/" << Time.now.strftime('%Y-%m-%d-%H%M%S-%N').to_s << ".zip" 
    zip = Zip::ZipFile.open(tmp_filename, Zip::ZipFile::CREATE) 
    zip.close 

    photos.each do |photo| 
    file_to_add = photo.photo.file 
    zip = Zip::ZipFile.open(tmp_filename) 
    zip.add("tmp/", file_to_add.path) 
    zip.close 
    end 

    #do the rest.. like send zip or upload file and e-mail link 

end 

Esto no funciona porque photo.photo.file devuelve una instancia de CarrierWave :: Almacenamiento :: :: Niebla archivo en lugar de un archivo normal.

EDIT: El error esto conduce a:

Errno :: ENOENT: No existe el fichero o directorio - uploads/fotos/name.jpg

También trataron los siguientes:

tmp_filename = "#{Rails.root}/tmp/" << Time.now.strftime('%Y-%m-%d-%H%M%S-%N').to_s << ".zip" 
    zip = Zip::ZipFile.open(tmp_filename, Zip::ZipFile::CREATE) 
    zip.close 

    photos.each do |photo| 
     processed_uri = URI.parse(URI.escape(URI.unescape(photo.photo.file.authenticated_url)).gsub("[", "%5B").gsub("]", "%5D")) 
     file_to_add = CarrierWave::Uploader::Download::RemoteFile.new(processed_uri) 
     zip = Zip::ZipFile.open(tmp_filename) 
     zip.add("tmp/", file_to_add.path) 
     zip.close 
    end 

Pero esto me da un 403. Algo de ayuda sería muy apreciada ... Probablemente no es tan difícil Solo lo estoy haciendo mal

+0

¿Qué gema estabas usando? Rubyzip joya? –

Respuesta

16

He conseguido resolver el problema con la ayuda de @ffoeg

La solución ofrecida por @ffoeg no funcionó bastante muy bien para mí, ya que estaba lidiando con archivos comprimidos> 500 MB que me causaron problemas en Heroku. para ello me he trasladado al comprimir a un proceso de fondo utilizando resque:

app/trabajadores/photo_zipper.rb:

require 'zip/zip' 
require 'zip/zipfilesystem' 
require 'open-uri' 
class PhotoZipper 
    @queue = :photozip_queue 

    #I pass 
    def self.perform(id_of_object_with_images, id_of_user_to_be_notified) 
    user_mail = User.where(:id => id_of_user_to_be_notified).pluck(:email) 
    export = PhotoZipper.generate_zip(id_of_object_with_images, id_of_user_to_be_notified) 

    Notifications.zip_ready(export.archive_url, user_mail).deliver 
    end 

    # Zipfile generator 
    def self.generate_zip(id_of_object_with_images, id_of_user_to_be_notified) 
    object = ObjectWithImages.find(id_of_object_with_images) 
    photos = object.images 
    # base temp dir 
    temp_dir = Dir.mktmpdir 
    # path for zip we are about to create, I find that ruby zip needs to write to a real file 
    # This assumes the ObjectWithImages object has an attribute title which is a string. 
    zip_path = File.join(temp_dir, "#{object.title}_#{Date.today.to_s}.zip") 

    Zip::ZipOutputStream.open(zip_path) do |zos| 
     photos.each do |photo| 
     path = photo.photo.path 
     zos.put_next_entry(path) 
     zos.write photo.photo.file.read 
     end 
    end 

    #Find the user that made the request 
    user = User.find(id_of_user_to_be_notified) 

    #Create an export object associated to the user 
    export = user.exports.build 

    #Associate the created zip to the export 
    export.archive = File.open(zip_path) 

    #Upload the archive 
    export.save! 

    #return the export object 
    export 
    ensure 

    # clean up the tempdir now! 
    FileUtils.rm_rf temp_dir if temp_dir 
    end 


end 

app/controllers/photos_controller.rb:

format.zip do 
    #pick the last ObjectWithImages.. ofcourse you should include your own logic here 
    id_of_object_with_images = ObjectWithImages.last.id 

    #enqueue the Photozipper task 
    Resque.enqueue(PhotoZipper, id_of_object_with_images, current_user.id) 

    #don't keep the user waiting and flash a message with information about what's happening behind the scenes 
    redirect_to some_path, :notice => "Your zip is being created, you will receive an e-mail once this process is complete" 
    end 

Muchas gracias a @ffoeg por ayudarme. Si tus cremalleras son más pequeñas, podrías probar la solución de @ffoeg.

+0

Hola, ¿archivo es una columna de una tabla db llamada export? ¿Cuál es el tipo de datos? ¿binario? Gracias – user1883793

10

Aquí está mi opinión. No puede haber errores tipográficos, pero creo que esta es la esencia de la misma :)

# action method, stream the zip 
def download_photos_as_zip # silly name but you get the idea 
    generate_zip do |zipname, zip_path| 
    File.open(zip_path, 'rb') do |zf| 
     # you may need to set these to get the file to stream (if you care about that) 
     # self.last_modified 
     # self.etag 
     # self.response.headers['Content-Length'] 
     self.response.headers['Content-Type'] = "application/zip" 
     self.response.headers['Content-Disposition'] = "attachment; filename=#{zipname}" 
     self.response.body = Enumerator.new do |out| # Enumerator is ruby 1.9 
     while !zf.eof? do 
      out << zf.read(4096) 
     end 
     end 
    end 
    end 
end 


# Zipfile generator 
def generate_zip(&block) 
    photos = Photo.all 
    # base temp dir 
    temp_dir = Dir.mktempdir 
    # path for zip we are about to create, I find that ruby zip needs to write to a real file 
    zip_path = File.join(temp_dir, 'export.zip') 
    Zip::ZipFile::open(zip_path, true) do |zipfile| 
    photos.each do |photo| 
     zipfile.get_output_stream(photo.photo.identifier) do |io| 
     io.write photo.photo.file.read 
     end 
    end 
    end 
    # yield the zipfile to the action 
    block.call 'export.zip', zip_path 
ensure 
    # clean up the tempdir now! 
    FileUtils.rm_rf temp_dir if temp_dir 
end 
+0

Muchas gracias por su respuesta. Estoy usando Ruby 1.9.3-p125 y me sale el error (después de corregir algunos errores tipográficos) de que zipfile.open llama a un método privado. (Método privado 'abierto 'llamado para # ) – Gidogeek

+0

hmm, verifico otro proyecto donde hice esto. – ffoeg

+0

¿Puedes mostrarme la línea en tu Gemfile donde traes la gema rubyzip? – ffoeg

Cuestiones relacionadas