将储存在S3上的所有Paperclip附件进行压缩

15

Paperclip是一个非常适用于Rails的上传插件,将上传内容存储在本地文件系统或Amazon S3上都能够很好地工作。虽然我更倾向于将文件存储在本地主机上,但由于该应用程序将托管在Heroku上,因此必须使用S3。

如何一次性地从S3获取所有上传/附件并进行压缩下载呢?

从本地文件系统获取文件并打包成zip似乎很简单。但是,从S3获取文件却让我感到困惑。我认为这可能与rubyzip处理URL引用的文件的方式有关。我尝试了各种方法,但似乎无法避免错误。

    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
          }

想知道这里的解决方案是否是使用AWS-S3 gem获取存储桶中的所有文件,而不使用paperclip? - chaserx
你几乎肯定想使用to_file()而不是url()。 - vladr
是的。我也想到了。出现了这个错误。无法将 Paperclip::Tempfile 转换为字符串。 - chaserx
2个回答

11

更新这个答案到 paperclip > 3.0,.to_file 方法已被移除 :( - damuz91
@vladr和@chaserx:大家好,很抱歉我有点困惑。这是我目前拥有的内容,但我似乎找不到copy_to_local_file方法的正确destination_url参数。`@user_attachments.each do |e|zip.add(e.document_file_name, e.document.copy_to_local_file(:original, "#{Rails.root}"))end`非常感谢任何帮助!谢谢! - humairatasnim

2

@vlard的解决方案是可以的。但是我遇到了一些关于to_file的问题。它会创建一个临时文件,垃圾回收器有时会在将文件添加到zip文件之前删除该文件。因此,我会随机出现Errno :: ENOENT:没有这样的文件或目录错误。

所以现在我正在使用以下代码(为了与初始问题保持一致,我保留了初始代码变量名称)

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
      }

你能解释一下为什么你的解决方案会强制垃圾回收器等待吗?谢谢! - Jared
1
我无法解释为什么它有效,但可以确认我曾经遇到完全相同的问题,这解决了它! - Oll
File.open方法中的b+是什么意思?我相信只有r(+)w(+)a(+) - Marc
@Jared,看一下传递给registrations_with_attachments.each的代码块的最后一行的注释:puts方法使用了file_to_add变量,因此在执行zip.add("abstracts/#{e.abstract.original_filename}", file_to_add.path)时,垃圾回收器被强制将该变量保留在内存中。 - Dorian
@Marc rb(+) = r(+) + b,其中b表示“二进制文件模式。在Windows上抑制EOL<->CRLF转换。并将外部编码设置为ASCII-8BIT,除非明确指定。” - Dorian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接