with rails multiple files ruby-on-rails ruby amazon-s3 zip carrierwave

ruby on rails - rails - Descarga y compresión de archivos que se cargaron en S3 con CarrierWave



rails image upload (2)

Aquí está mi toma. Podría haber errores tipográficos, pero creo que esto es lo esencial :)

# 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

Tengo una pequeña aplicación Rails 3.2.1 que usa CarrierWave 0.5.8 para subir archivos a S3 (usando Fog)

Quiero que los usuarios puedan seleccionar algunas imágenes que les gustaría descargar, luego comprimirlas y enviarles una postal. Esto es lo que he encontrado:

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 :: Storage :: Fog :: File en lugar de un archivo normal.

EDITAR: El error que esto lleva a:

Errno :: ENOENT: No existe tal archivo o directorio - cargas / fotos / nombre.jpg

También probé lo siguiente:

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. Alguna ayuda sería muy apreciada. Probablemente no sea tan difícil. Simplemente lo estoy haciendo mal.


He logrado resolver el problema con la ayuda de @ffoeg

La solución ofrecida por @ffoeg no funcionó tan bien para mí ya que estaba tratando con archivos zip> 500 MB, lo que me causó problemas en Heroku. Por lo tanto, moví la compresión a un proceso en segundo plano usando resque :

app / workers / 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, puedes probar la solución de @ffoeg.