1. ya-mada

    Posted

    ya-mada
Changes in title
+RubyでZipを使ってやる!サンプル集
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,201 @@
+# はじめに
+
+rubyでzipを使おうとするとziprubyとrubyzipがググれると思います。
+サンプルもたくさん出てきます。
+しかし「やりたいのこれじゃないんだよ!」と皆さん思いませんでしたか?
+ということで、rubyzipのSource読みつつ痒いところに手が届く事を目指して苦労した部分を纏めてみました。
+
+## 目次
+
+* rubyzipの理由
+* 設定
+* 圧縮
+* 解凍
+* パスワード付きZIPの扱い(業務アプリなら必須だよね!
+* zipにファイル追加(業務アプリだとやりたいじゃん!
+* 文字化け対策
+
+## ziprubyではなくrubyzipの理由
+
+ziprubyの更新が止まっているから。。。
+
+
+## 設定
+
+gemにrubyzipを指定
+
+```rb
+gem 'rubyzip', '1.1.7'
+```
+
+zipをrequire する
+
+```rb
+require 'zip'
+```
+
+## 圧縮
+
+* 方式1:Zip::OutputStreamを使う
+
+```rb
+ def compress(path, zippath)
+ # Stream開く
+ buffer = Zip::OutputStream.write_buffer(::StringIO.new('')) do |out|
+ out.put_next_entry(File.basename(path))
+ # ワンラインで書けたけど、ファイル名が長い?とエラーになるのでStreamを一旦変数に受ける・・・謎
+ file_buf = File.open(path) { |e| e.read }
+ out.write file_buf
+ end
+ # Stream書き出す
+ File.open(zippath) { |f| f.write(buffer.string) }
+ zippath
+ end
+```
+
+* 方式2:Zip::File.open(${ファイル名}, Zip::File::CREATE)を使う
+
+こっちのほうが実装は楽だがパスワードとかは使えない
+
+```rb
+ # Zip圧縮
+ def compress(path, zippath)
+ File.unlink zippath if File.file?(zippath)
+ Zip::File.open(zippath, Zip::File::CREATE) do |z_fp|
+ z_fp.add(File.basename(path), path)
+ end
+ outpath
+ end
+```
+
+## 解凍
+
+```rb
+ # Zip解凍
+ def uncompress(path, outpath)
+ entrys = []
+ Dir.mkdir(outpath) unless Dir.exist?(outpath)
+ # 2つ目の引数はoffset
+ Zip::InputStream.open(path, 0) do |input|
+ # get_next_entryするとinputのoffset(ポインタ)が動く
+ while (entry = input.get_next_entry)
+ # 書き出し先を作る
+ save_path = File.join(outpath, entry.name)
+ File.open(save_path) do |wf|
+ # get_next_entryでポインタが動いているので、毎回input.readでOK
+ wf.puts(input.read)
+ end
+ entrys << save_path
+ end
+ end
+ # 解凍されたファイルたちを返却する
+ entrys
+ end
+```
+
+## パスワード付きZIPの扱い
+
+### 圧縮
+
+```rb
+ def compress(path, zippath, password )
+ # パスワードのオブジェクト作る
+ encrypter = password.present? ? Zip::TraditionalEncrypter.new(password) : nil
+
+ # Stream開く&パスワードを渡す
+ # Zip::File.openはTraditionalEncrypterを受けるIFが(多分)ないので、使えない
+ buffer = Zip::OutputStream.write_buffer(::StringIO.new(''), encrypter) do |out|
+ out.put_next_entry(File.basename(path))
+ # ワンラインで書けたけど、ファイル名が長い?とエラーになるのでStreamを一旦変数に受ける・・・謎&Windowsだから?
+ file_buf = File.open(path) { |e| e.read }
+ out.write file_buf
+ end
+ # Stream書き出す
+ File.open(zippath) { |f| f.write(buffer.string) }
+ zippath
+ end
+```
+
+### 解凍
+
+```rb
+ # Zip解凍
+ def uncompress(path, outpath, password )
+ entrys = []
+ Dir.mkdir(outpath) unless Dir.exist?(outpath)
+ # パスワードのオブジェクト作る
+ decrypter = password.present? ? Zip::TraditionalDecrypter.new(password) : nil
+
+ Zip::InputStream.open(path, 0, decrypter ) do |input|
+ # get_next_entryするとinputのoffset(ポインタ)が動く
+ while (entry = input.get_next_entry)
+ # 書き出し先を作る
+ save_path = File.join(outpath, entry.name)
+ File.open(save_path) do |wf|
+ # get_next_entryでポインタが動いているので、毎回input.readでOK
+ wf.puts(input.read)
+ end
+ entrys << save_path
+ end
+ end
+ # 解凍されたファイルたちを返却する
+ entrys
+ end
+```
+
+## zipにファイル追加
+
+```rb
+ # Zip圧縮(ファイル追加)
+ def modify(path, zippath, password)
+ # 追加するファイルの名前
+ addfile_name = File.basename(path)
+ # パスワードのオブジェクト作る
+ decrypter = nil
+ encrypter = nil
+ if password.present?
+ decrypter = Zip::TraditionalDecrypter.new(password)
+ encrypter = Zip::TraditionalEncrypter.new(password)
+ end
+
+ entrys = []
+ # 出力するZip用のStreamを開く
+ buffer = Zip::OutputStream.write_buffer(::StringIO.new(''), encrypter) do |out|
+ # ファイルを追加したいZipのStreamを開く
+ Zip::InputStream.open(zippath, 0, decrypter) do |input|
+ # inputの内容をoutにコピー
+ while (entry = input.get_next_entry)
+ out.put_next_entry(entry.name)
+ file_buf = input.read
+ out.write file_buf
+ entrys << entry.name
+ end
+ end
+ # inputのentryに追加するファイル名がなければ追加
+ unless entrys.include?(addfile_name)
+ out.put_next_entry(addfile_name)
+ file_buf = File.open(path, 'rb') { |e| e.read }
+ out.write file_buf
+ end
+ end
+ File.open(zippath, 'wb') { |f| f.write(buffer.string) }
+ zippath
+ end
+```
+
+## 文字化け対策
+
+基本的にバイナリモード使ってください
+
+```rb
+File.open(path, 'rb'
+File.open(zippath, 'wb')
+```
+
+これでもダメならエンコード指定してください
+
+```rb
+File.open(path, 'rb:utf-8'
+File.open(zippath, 'wb:utf-8')
+```
+