1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ruby(Rails)でsjisのパスワード付きファイルをunzipする

Last updated at Posted at 2018-02-04

Rubyでパスワード付きのzipファイルを解凍する案件があり色々しらべてみたが、解凍の情報って案外すくないので、最近の方法をまとめてみた。

まず、調べて出てくるのは以下の方法
rubyzip
zipruby
archive-zip
④外部コマンド unar

【結論】
④unar外部コマンドを利用して一時ディレクトリに出力してから処理する。(エラ-処理は無視してます)

app/models/concerns/unzip.rb
require 'tmpdir'

module Unzip
  extend ActiveSupport::Concern

  class_methods do
    def tmpzipfiles(path, password)
      Dir.mktmpdir do |dir|
        system("unar -f -o '#{dir}' -p '#{password}' '#{path}'")
        Dir.glob(File.join(dir, "**/*")){ |f| yield(f) }
      end
    end
  end
end

Railsでの使い方はモデル内で

app/models/model.rb
include Unzip

def hoge 
  tmpzipfiles do |f|
    # f(一時ファイル名)に対する処理
  end
end

以下は色々試した経緯です。

どちらもgemで入れられるライブラリーだが、②ziprubyはどうやらオワコンらしい。
ってことで一番メジャーそうな①rubyzipを使ってみる。

Railsなので、

Gemfile
gem 'rubyzip'

一時ディレクトリに解凍してブロック処理を行い、終わったら削除としたいので、
Dir::mktmpdirを利用

app/models/concerns/unzip.rb
require 'zip'
require 'tmpdir'

module Unzip
  extend ActiveSupport::Concern

  class_methods do
    def tmpzipfiles(path, password)
      entrys = []
      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)
          Dir.mktmpdir do |dir|
            save_path = File.join(dir, entry.name)
            File.open(save_path, "wb") {|wf| wf.puts(input.read) }
            yield(save_path)
          end
        end
      end
    end
  end
end

としたんだが、どうもSJISの1文字だけのファイル("0\n\r")が正しく解凍できない。
それに、このコードがどうも嫌だ。input.get_next_entryって!そんで取得したentryではなく、inputからreadするところが、とっても嫌なので却下!

次に③のarchive-zip使ってみる。これは殆ど情報がないけど、シンプルが売りで使いやすそうですね。

Gemfile
gem 'archive-zip'
app/models/concerns/unzip.rb
require 'archive/zip'
require 'tmpdir'

module Unzip
  extend ActiveSupport::Concern

  class_methods do
    def tmpzipfiles(path, password)
      Dir.mktmpdir do |dir|
        Archive::Zip.extract(path, dir, password: password)
        Dir.glob(File.join(dir, "**/*")){ |f| yield(f)  }
      end
    end
  end
end

こっちは3バイトファイルも問題なし、しかもコードもシンプルになりました。
しかし、ZIPファイル内にSJISの日本語ファイルとかフォルダがあると文字化けしてしまいます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?