开发者

Zip up all Paperclip attachments stored on S3

开发者 https://www.devze.com 2022-12-21 13:01 出处:网络
Paperclip is a great upload plugin for Rails. Storing uploads on the local filesystem or Amazon S3 seems to work well. I\'d just assume store files on the localhost, but the use of S3 is required for

Paperclip is a great upload plugin for Rails. Storing uploads on the local filesystem or Amazon S3 seems to work well. I'd just assume store files on the localhost, but the use of S3 is required for this app as it will be hosted on Heroku.

How would I go about getting all of my uploads/attachments from S3 in a single zipped download?

Getting a zip of files from the local filesystem seems straight forward. It's getting the files from S3 that has me puzzled. I think it may have something to do with the way that rubyzip handles files referenced by URL. I've tried various approaches but can't seem to avoid errors.

    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
                  regist开发者_JAVA技巧rations_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
          }


You almost certainly want to use e.abstract.to_file.path instead of e.abstract.url(...).

See:

  • Paperclip::Storage::S3::to_file (should return a TempFile)
  • TempFile::path

UPDATE

From the changelog:

New in 3.0.1:

  • API CHANGE: #to_file has been removed. Use the #copy_to_local_file method instead.


@vlard's solution is ok. However I've run into some issues with the to_file. It creates a tempfile and the garbage collector deletes (sometimes) the file before it was added to the zip file. Therefor, I'm getting random Errno::ENOENT: No such file or directory errors.

So I'm using the following code now (I've kept the initial code variables names for consistency with the initial question)

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

精彩评论

暂无评论...
验证码 换一张
取 消