简体   繁体   English

使用CarrierWave下载和压缩上传到S3的文件

[英]Downloading and zipping files that were uploaded to S3 with CarrierWave

I have a small Rails 3.2.1 app that uses CarrierWave 0.5.8 for file uploads to S3 (using Fog) 我有一个小的Rails 3.2.1应用程序使用CarrierWave 0.5.8将文件上传到S3(使用Fog)

I want users to be able to select some images that they'd like to download, then zip them up and send them a zip. 我希望用户能够选择他们想要下载的一些图像,然后将它们压缩并发送一个拉链。 Here is what I've come up with: 这是我提出的:

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

This doesn't work because photo.photo.file returns an instance of CarrierWave::Storage::Fog::File instead of a regular file. 这不起作用,因为photo.photo.file返回CarrierWave :: Storage :: Fog :: File的实例而不是常规文件。

EDIT: The error this leads to: 编辑:这导致的错误:

Errno::ENOENT: No such file or directory - uploads/photos/name.jpg Errno :: ENOENT:没有这样的文件或目录 - uploads / photos / name.jpg

I also tried the following: 我也尝试过以下方法:

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

But this gives me a 403. Some help would be greatly appreciated.. It probably is not that hard I'm just Doing it Wrong™ 但是这给了我一个403.一些帮助将不胜感激..它可能并不那么难我只是做它错了™

I've managed to solve the problem with help from @ffoeg 我已经设法在@ffoeg的帮助下解决了这个问题

The solution offered by @ffoeg didn't work quite so well for me since I was dealing with zip files > 500 MB which caused me problems on Heroku. @ffoeg提供的解决方案对我来说效果不是很好,因为我处理的是> 500 MB的zip文件,这导致我在Heroku上遇到问题。 I've therefor moved the zipping to a background process using resque : 我因此使用resque将压缩移动到后台进程:

app/workers/photo_zipper.rb: 应用程序/工人/ 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: 应用程序/控制器/ 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

Many thanks to @ffoeg for helping me out. 非常感谢@ffoeg帮助我。 If your zips are smaller you could try @ffoeg's solution. 如果你的拉链较小,你可以试试@ ffoeg的解决方案。

Here is my take. 这是我的看法。 There could be typos but I think this is the gist of it :) 可能有拼写错误,但我认为这是它的要点:)

# 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM