Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
20
Help us understand the problem. What is going on with this article?
@ayies128

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

More than 3 years have 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の使い方は結構ググっても出てこない
20
Help us understand the problem. What is going on with this article?
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
ayies128
株式会社illustrious CTO。 EC業務を色々な角度から効率化できるように日々システム開発を行っています。 システム開発の話から飲みに行こうって話までなんでもどうぞ。 うどんとお酒と #UVERWorld が大好きです。 世界で売るためのECインフラサービス「lismoa」の開発運営を行なっています。 https://lismoa.com/

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
20
Help us understand the problem. What is going on with this article?