使用RubyZip生成文件并以zip格式下载

时间:2019-02-05 17:43:16

标签: ruby-on-rails file base64 zipfile rubyzip

对于我的Ruby on Rails项目(Rails版本5.1.2),我正在生成图像文件(png),并使用RubyZip gem将其作为zip文件下载。

图像文件未存储在任何目录中。我有一个称为附件的模型。每个附件都有一个属性image_string,它是图像的base64字符串。您可以使用image_tag(src = "data:image/jpeg;base64, #{attachment.image_string}", style: "border-radius: 0;")

这样的标签显示图像

对于多个图像,我想为每个图像创建一个临时文件而不将它们存储在任何地方,并将这些图像下载为zip文件。

我现在拥有的代码:

def bulk_download
  require('zip')
  ::Zip::File.open("/tmp/mms.zip", Zip::File::CREATE) do |zipfile|
    Attachment.all.each do |attachment|
      image_file = Tempfile.new("#{attachment.created_at.in_time_zone}.png")
      image_file.write(attachment.image_string)
      zipfile.add("#{attachment.created_at.in_time_zone}.png", image_file.path)
    end
  end
  send_file "/tmp/mms.zip", type: 'application/zip', disposition: 'attachment', filename: "my_archive.zip"
  respond_to do |format |
    format.all { head :ok, content_type: "text/html" }
  end
end

但是下载的zip文件中没有文件,并且大小为0字节。 预先感谢。

3 个答案:

答案 0 :(得分:1)

您应该像这样关闭并取消链接zip文件:

require('zip')

class SomeController < ApplicationController
  # ...

  def bulk_download
    filename = 'my_archive.zip'
    temp_file = Tempfile.new(filename)

    begin
      Zip::OutputStream.open(temp_file) { |zos| }

      Zip::File.open(temp_file.path, Zip::File::CREATE) do |zip|
        Attachment.all.each do |attachment|
          image_file = Tempfile.new("#{attachment.created_at.in_time_zone}.png")
          image_file.write(attachment.image_string)
          zipfile.add("#{attachment.created_at.in_time_zone}.png", image_file.path)
        end
      end

      zip_data = File.read(temp_file.path)
      send_data(zip_data, type: 'application/zip', disposition: 'attachment', filename: filename)
    ensure # important steps below
      temp_file.close
      temp_file.unlink
    end
  end
end

这是一篇很好的博客文章,我用作该代码的来源:https://thinkingeek.com/2013/11/15/create-temporary-zip-file-send-response-rails/

此外,优良作法是将所有库要求都保留在文件(即require('zip'))的顶部。

答案 1 :(得分:1)

公认的解决方案确实是正确的。但是,我将扩展已经提供的解决方案,使其与 .slick-arrow.slick-prev { font-size: 10px; } .ant-carousel .slick-prev::before { content: '<'; display: block; position: relative; bottom: 20px; right: 3px; /* width: 100px; */ font-size: 25px; color: gray; background-color: white; border: 2px solid gray; display: flex; justify-content: center; align-items: center; padding: 5px; border-radius: 50%; width: 33px; height: 33px; } .slick-arrow.slick-next { font-size: 10px; } .ant-carousel .slick-next::before { content: '>'; display: block; position: relative; right: 3px; bottom: 20px; /* width: 100px; */ font-size: 25px; color: gray; background-color: white; border: 2px solid gray; display: flex; justify-content: center; align-items: center; padding: 5px; border-radius: 50%; width: 33px; height: 33px; } 附件一起使用。
在使用已接受的解决方案时,我发现 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> 方法不适用于 ActiveStorage 附件并引发这样的错误

image_string

假设我们有一个名为 ActiveStorage 的 Rails 模型,其 ActiveStorage 属性名为 NoMethodError - undefined method `image_string' for #<ActiveStorage::Attached::One:0x00007f78cc686298>

Product

为了使其适用于 ActiveStorage 附件,我们需要按如下方式更新代码

attachment

答案 2 :(得分:0)

它对我有用(我需要基于 Carrierwave 加载 MyModel 文档):

require 'zip'
require 'open-uri'

class TestsController < ApplicationController
  def index
    filename = 'test.zip'
    temp_file = ::Tempfile.new(filename)

    my_model_document = ::MyModel.last
    my_model_document_name = ::File.basename(my_model_document.document.path)

    begin
      ::Zip::OutputStream.open(temp_file) { |zos| }
      ::Zip::File.open(temp_file.path, ::Zip::File::CREATE) do |zipfile|
        dr_temp_file = Tempfile.new(my_model_document_name)
        dr_temp_file.write(open(my_model_document.document.url).read.force_encoding("UTF-8"))
        zipfile.add(my_model_document_name, dr_temp_file.path)
      end

      zip_data = File.read(temp_file.path)
      send_data(zip_data, type: 'application/zip', disposition: 'attachment', filename: filename)
    ensure
      temp_file.close
      temp_file.unlink
    end
  end
end