Help us understand the problem. What is going on with this article?

Rails rubyzipでディレクトリを圧縮してダウンロードする

More than 1 year has passed since last update.

はじめに

Ruby on Rails Advent Calendar 2017 - Qiita の5日目の記事です。
うまいRailsの使い方などを紹介したかったのですが、全然思い浮かびませんでした:man_tone1::man_tone1::man_tone1:
zipを作成するgemの紹介でご勘弁を:bow::bow_tone1::bow_tone2::bow_tone3::bow_tone4::bow_tone5:

やりたいこと

こんな感じのディレクトリを含むデータを
スクリーンショット 2017-10-06 18.12.50.png

圧縮したい!!
スクリーンショット 2017-10-06 18.13.11.png

使用するgem

rubyzip
導入はREADMEを読みましょう。

ziprubyってのもあるから間違えないように。

rubyzipの基本的な使い方

READMEに書いてある基本的なzip作成方法は以下の通り
Basic zip archive creation

require 'rubygems'
require 'zip'

folder = "Users/me/Desktop/stuff_to_zip"
input_filenames = ['image.jpg', 'description.txt', 'stats.csv']

zipfile_name = "/Users/me/Desktop/archive.zip"

Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
  input_filenames.each do |filename|
    # Two arguments:
    # - The name of the file as it will appear in the archive
    # - The original file, including the path to find it
    zipfile.add(filename, File.join(folder, filename))
  end
  zipfile.get_output_stream("myFile") { |os| os.write "myFile contains just this" }
end

手始めにサンプルを少しいじってローカルにある画像ファイル数個を圧縮してみました。
簡単にできました。
その時こう思ったのです。
「あ、これは勝ちましたね。」と...

そして英語だからってまともにREADMEを読まずに、zipfile.addでディレクトリ渡せばできるんじゃ?
という軽い気持ちで多方面からaddを試してみたんですが、ことごとく敗北。
負けました。

実装

readmeの少し下を見てみましょう。
Zipping a directory recursively....ん?流れ変わったな?
「再帰的にディレクトリを圧縮する」by Google翻訳
以下ソース

require 'zip'

# This is a simple example which uses rubyzip to
# recursively generate a zip file from the contents of
# a specified directory. The directory itself is not
# included in the archive, rather just its contents.
#
# Usage:
#   directory_to_zip = "/tmp/input"
#   output_file = "/tmp/out.zip"
#   zf = ZipFileGenerator.new(directory_to_zip, output_file)
#   zf.write()
class ZipFileGenerator
  # Initialize with the directory to zip and the location of the output archive.
  def initialize(input_dir, output_file)
    @input_dir = input_dir
    @output_file = output_file
  end

  # Zip the input directory.
  def write
    entries = Dir.entries(@input_dir) - %w(. ..)

    ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |io|
      write_entries entries, '', io
    end
  end

  private

  # A helper method to make the recursion work.
  def write_entries(entries, path, io)
    entries.each do |e|
      zip_file_path = path == '' ? e : File.join(path, e)
      disk_file_path = File.join(@input_dir, zip_file_path)
      puts "Deflating #{disk_file_path}"

      if File.directory? disk_file_path
        recursively_deflate_directory(disk_file_path, io, zip_file_path)
      else
        put_into_archive(disk_file_path, io, zip_file_path)
      end
    end
  end

  def recursively_deflate_directory(disk_file_path, io, zip_file_path)
    io.mkdir zip_file_path
    subdir = Dir.entries(disk_file_path) - %w(. ..)
    write_entries subdir, zip_file_path, io
  end

  def put_into_archive(disk_file_path, io, zip_file_path)
    io.get_output_stream(zip_file_path) do |f|
      f.write(File.open(disk_file_path, 'rb').read)
    end
  end
end

「Copy from here」と書いていますし言われるがままにこのクラスをプロジェクトに配置します。
すると、素晴らしい。
インスタンスを作成してwriteメソッドを呼び出すだけで指定したフォルダがzipになるのです。

zip_file_generator = ZipFileGenerator.new(input_dir, output_file)
zip_file_generator.write
# ディレクトリのzipができる

後はコントローラーでsend_fileを使ってダウンロードさせるだけです。

send_file output_file

所感

  • READMEはヨンダホウガイイデスヨ
  • なんでgemのライブラリに含んでくれないんですかねえ
  • rubyzipの使い方は結構ググっても出てこない
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした