rubyzipについて
rubygemsでインストールできるZip圧縮ユーティリティ、rubyzipというものがある。ディレクトリごと圧縮したい場合はrubyzipを使うと簡単。ただ、rubyzipはGPLなので注意しないといけない。rubygems経由で使う分はGPLライセンスの影響は受けないと思うが、ソフトウェアにソースコードを組み込むと(gemディレクトリから抜き出してRailsのvendor以下に入れたり、libに入れたりとか)GPLライセンスが感染してしまうだろう。rubyzipはRubyと同じライセンスなので、vendor以下などに入れてもいいみたい。
#rubyzipのインストール
gem install rubyzip
ファイル・ディレクトリの圧縮はZipFileクラスのopenメソッドでzipファイルを開いて、addメソッドでファイルエントリを追加していく。
展開はZipInputStreamクラスのopenメソッドでzipファイルを開いてから、get_next_entryメソッドでエントリをひとつずつ取得して、readメソッドで中身を取得する。
注意しないといけないのは、zipファイルに登録するエントリのファイル名に使う文字コードである。Windowsの場合はShift_JISにしないと解凍したときにファイル名が化ける、LinuxだとUTF-8など環境に合わせてやる必要がある。
以下は簡単な例。ZipFileUtils.zipとZipFileUtils.unzipがそれぞれ圧縮と解凍。ディレクトリを圧縮することも可能。options引数は:fs_encodingでファイルシステムのエンコードをUTF-8、Shift_JIS、EUC-JPから指定する。
require 'rubygems' require 'kconv' require 'zip/zipfilesystem' require 'fileutils' module ZipFileUtils # src file or directory # dest zip filename # options :fs_encoding=[UTF-8,Shift_JIS,EUC-JP] def self.zip(src, dest, options = {}) src = File.expand_path(src) dest = File.expand_path(dest) File.unlink(dest) if File.exist?(dest) Zip::ZipFile.open(dest, Zip::ZipFile::CREATE) {|zf| if(File.file?(src)) zf.add(encode_path(File.basename(src), options[:fs_encoding]), src) break else each_dir_for(src){ |path| if File.file?(path) zf.add(encode_path(relative(path, src), options[:fs_encoding]), path) elsif File.directory?(path) zf.mkdir(encode_path(relative(path, src), options[:fs_encoding])) end } end } end # src zip filename # dest destination directory # options :fs_encoding=[UTF-8,Shift_JIS,EUC-JP] def self.unzip(src, dest, options = {}) FileUtils.makedirs(dest) Zip::ZipInputStream.open(src){ |is| loop do entry = is.get_next_entry() break if entry.nil?() dir = File.dirname(entry.name) FileUtils.makedirs(dest+ '/' + dir) path = encode_path(dest + '/' + entry.name, options[:fs_encoding]) if(entry.file?()) File.open(path, File::CREAT|File::WRONLY|File::BINARY) do |w| w.puts(is.read()) end else FileUtils.makedirs(path) end end } end private def self.each_dir_for(dir_path, &block) dir = Dir.open(dir_path) each_file_for(dir_path){ |file_path| yield(file_path) } end def self.each_file_for(path, &block) if File.file?(path) yield(path) return true end dir = Dir.open(path) file_exist = false dir.each(){ |file| next if file == '.' || file == '..' file_exist = true if each_file_for(path + "/" + file, &block) } yield(path) unless file_exist return file_exist end def self.relative(path, base_dir) path[base_dir.length() + 1 .. path.length()] if path.index(base_dir) == 0 end def self.encode_path(path, encode_s) return path if encode_s.nil?() case(encode_s) when('UTF-8') return path.toutf8() when('Shift_JIS') return path.tosjis() when('EUC-JP') return path.toeuc() else return path end end end
使い方の例
#c:\rubyをruby.zipに圧縮 ZipFileUtils.zip('c:/ruby', 'c:/ruby.zip') #ruby.zipをc:\rubyに展開 ZipFileUtils.unzip('c:/ruby.zip', 'c:/ruby') #c:\rubyをruby.zipにファイル名のエンコードをShift_JISに指定して圧縮 ZipFileUtils.zip('c:/ruby', 'c:/ruby.zip', {:fs_encoding => 'Shift_JIS'}) #ruby.zipをc:\rubyにファイル名のエンコードをShift_JISに指定して展開 ZipFileUtils.unzip('c:/ruby.zip', 'c:/ruby', {:fs_encoding => 'Shift_JIS'})