2012-03-28 84 views
19

我有一个小的Rails使用CarrierWave 0.5.8文件上传到S3 3.2.1应用程序(使用雾)下载和压缩和解文件被上传到S3与CarrierWave

我希望用户能够选择一些他们想要下载的图片,然后将其压缩并发送给他们一个zip文件。以下是我想出来的:

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 

这不起作用,因为photo.photo.file返回CarrierWave ::存储::雾::文件,而不是一个常规文件的一个实例。

编辑:错误,这导致:

变量Errno :: ENOENT:没有这样的文件或目录 - 上传/照片/ name.jpg

我也试过如下:

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 

但是,这给了我一个403.一些帮助将不胜感激..它可能不是那么难我只是做它错误™

+0

您使用的是什么宝石?红宝石宝石? – 2013-08-26 18:20:52

回答

16

我已经成功的帮助来解决问题这对我来说很好,因为我正在处理> 500 MB的zip文件,这在Heroku上导致了我的问题。我为此感动荏苒到后台进程使用resque

应用程序/工人/ 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 

应用程序/控制器/ 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 

非常感谢@ffoeg帮助我。如果你的拉链较小,你可以尝试@ffoeg的解决方案。

+0

嗨,归档名为出口的数据库表的列?什么是数据类型?二进制?谢谢 – user1883793 2013-10-25 01:24:05

10

这是我的。有可能是拼写错误,但我认为这是从@ffoeg

通过@ffoeg提供的解决方案并没有完全工作要点:)

# 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

非常感谢您的回复。我正在使用Ruby 1.9.3-p125,并且我得到了zipfile.open调用私有方法的错误(纠正了一些拼写错误之后)。 (私人方法'打开'要求#) – Gidogeek 2012-03-29 15:14:15

+0

嗯,我会检查另一个项目,我这样做。 – ffoeg 2012-03-29 16:44:26

+0

你能告诉我在你的Gemfile中,你带rubyzip宝石的行吗? – ffoeg 2012-03-29 16:45:28