Techioz Blog

Zlib Gunzip は部分的なファイルのみを返します

概要

27MB の .gz ファイル (解凍すると 127MB) があります。 Ruby の Zlib を使用してファイルを解凍すると、正しくフォーマットされたデータが返されますが、ファイルは予想サイズ (253,000 行のデータのうち 1290 行) の一部に切り詰められます。

string_io = StringIO.new(body)
file = File.new("test.json.gz", "w+")
file.puts string_io.read
file.close

# string_io.read.length == 26_675_650
# File.size("test.json.gz") == 27_738_775

GzipReader の使用:

data = ""
File.open(file.path) do |f|
  gz = Zlib::GzipReader.new(f)
  data << gz.read
  gz.close
end
# data.length = 603_537

別の GzipReader メソッドを使用する:

data = ""
Zlib::GzipReader.open(file.path) do |gz|
  data << gz.read
end
# data.length == 603_537

ガンジップの使用:

gz = Zlib.gunzip(string_io.read)
# gz.length == 603_537

予想されるサイズは 127,604,690 ですが、603,537 しか抽出できません。端末でgunzipを使用するとファイル全体が正しく抽出されますが、これをプログラムで処理する方法を探しています。

解決策

ファイルを開いてファイル ハンドラーを渡す代わりに、Zlib::GzipReader.open() を使用してみましたか?ここに文書化されています https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipReader.html

ローカルでテストしたところ、適切な結果が得られました。

data = ''
=> ""

Zlib::GzipReader.open('file.tar.gz') { |gz|
  data << gz.read
}

data.length
=> 750003

次に、非圧縮時のファイルサイズを確認しました。

gzip -l file.tar.gz                                                                                                                           
  compressed uncompressed  ratio uncompressed_name
      315581       754176  58.1% file.tar

編集: S3 API 経由でデータを取得しているという更新を見ました。ファイルに書き込む前に、本文を Base64 デコードしていることを確認してください。