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

rubyzipでディレクトリが圧縮できない事がある不具合を解決する

Ruby on Rails でディレクトリをzipで圧縮しようとしたときに遭遇したエラーのお話です。
rubyzipというgemを使って実装する際、ディレクトリをzip圧縮するサンプルスクリプトが含まれていたので、
その通り導入してみたのですが...

サンプルスクリプトでは圧縮できなかった

大きく以下の問題でエラーが発生しました。(箇条書きですみません...)

  1. 日本語のディレクトリ名を使った場合に文字化けする
  2. ディレクトリの合計容量が大きかった場合に、処理が途中でコケる

調整後のスクリプト

zip_file_generator.rb
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.unicode_names = true
        ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
            write_entries entries, '', zipfile
        end
    end

    private

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

            if File.directory? disk_file_path
                recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
            else
                put_into_archive(disk_file_path, zipfile, zipfile_path)
            end
        end
    end

    def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
        zipfile.mkdir zipfile_path.encode('cp932')
        subdir = Dir.entries(disk_file_path) - %w(. ..)
        write_entries subdir, zipfile_path, zipfile
    end

    def put_into_archive(disk_file_path, zipfile, zipfile_path)
        zipfile.get_output_stream(zipfile_path.encode('cp932')) do |f|
            #f.write(File.open(disk_file_path, 'rb').read)
            IO.copy_stream(File.open(disk_file_path, 'rb'), f)
        end
    end
end

まとめ

文字化けのためcp932でエンコーディングを行い、
大きなファイルを全てメモリに蓄えてしまわないよう、ストリーミング処理に変更しました。
誰かの参考になれば。

kenkubomi
Web系が得意なエンジニアです。フロントエンドはまだまだ勉強中。 ネイティブアプリも作ってみたくて、個人で「鳥取なにたべ!」アプリを配信しています。 投稿されている内容はあくまで私自身の見解であり、所属団体・企業を代表するものではありません。
https://www.wantedly.com/projects/448223
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