質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

1回答

1256閲覧

PHP: stream_filter_appendを使ったgzファイルのストリーム処理が環境で動かなくなる原因

muteki_gamer

総合スコア157

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

1グッド

1クリップ

投稿2021/07/19 10:16

ストリーム処理の実装でstream_filter_appendを使った際に環境によって動きが異なる現象が起こりました。
解決法は色々試した結果見つかりましたが、コードの意味が理解できませんでした。

S3からgzファイルを取得し、ストリーム処理を行おうとした時に発生した現象です。下記のコードのように実装した際にローカル環境(docker)上では正常に全行処理することができました。しかし、EC2上に作った同じバージョンのPHPで実行した時に10行ほど処理した後にfgetsでfalseが帰ってくるようになり、処理が終わるという現象にぶつかりました。

リンクを参考に["window" => 31]にするとEC2上でも動くようになりましたが、そもそもstream_filter_appendの第4引数の役割がドキュメントの説明で理解できていません。
PHP:圧縮フィルタにも書いてありますが、圧縮用ループバックウィンドウが32で動かず、31で動いた理由。また、環境によってはwindow=32でも動いた理由がわかる方がいましたら、ご教授願いたいです。

よろしくお願いいたします。

実装

$s3 = new Aws\S3\S3Client([ 'credentials' => [ 'key' => $_ENV['AWS_ACCESS_KEY_ID'], 'secret' => $_ENV['AWS_SECRET_ACCESS_KEY'], ], 'version' => 'latest', 'region' => 'ap-northeast-1', ]); $s3->registerStreamWrapper(); $key = "key.gz"; $stream = fopen('s3://'.$_ENV['S3_BUCKET'].'/'.$key, "r"); stream_filter_append( $stream, 'zlib.inflate', STREAM_FILTER_READ, ["window" => 32] ); while ($line = fgets($stream)) { var_dump($line); }
mpyw👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

(下部に訂正あり)

window の意味は,大雑把に言うと「圧縮する単位」です。圧縮処理を行うためにはストリームから読み出してメモリ上にデータを乗せる必要がありますが,1回の圧縮のためにメモリ上に読み出す最大量を示していると思います。

2の対数としての表現なので

2^32 (bytes) = 4294967296 (bytes) = 4 (GiB) ≒ 4.3 (GB)

となりますが, 4GB もメモリを使っていいんでしょうかね?ストリーム圧縮にしては体感的に載せすぎなイメージがあります…

ところで zlib.inflate のC言語での実装を読んでみると気になる記述がありました。

どうやら想定している値は 8 から 15 のようですね。

2^8 (bytes) = 256 (bytes) 2^15 (bytes) = 32768 (bytes) = 32 (KiB) ≒ 33 (KB)

圧縮単位としては妥当な感じじゃないでしょうか?エラーにならずに動いている理由まではわかりませんが,少なくとも 8 から 15 の範囲から外れた値を指定したときの動作は保証しないと言っているように見えます…

【追記 & 訂正】

見るファイル間違ってました,すいません。ストリームフィルタはこちらですね。

コメントを見た感じだと 9 から 15 の想定に見えますが…
(その下の MAX_WBITS を使った if 文の意味がイマイチよく分かってない)

投稿2021/07/21 02:37

編集2021/07/21 03:07
mpyw

総合スコア5223

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

muteki_gamer

2021/07/21 03:12 編集

回答ありがとうございます。メモリの使用量4GBはまずいですね。 現在動かしている環境が物理メモリ1GBなので、そんなに使っているとは気づきませんでした。 window=>32だとメモリが足りずに処理が失敗している可能性はありそうですね。 質問のリンク部分のテストコードではwindow => 31のような直接な記述はなく、window => 15+16のような記述の仕方で書いてあったので、これは想定が9~15だからなのかとしっくり来ました。一旦この範囲でテストして見て動作検証をしてみようと思います。 僭越ながら、めちゃくちゃ難しいことをしているというのをTwitterの方で拝見しました。 やはり一旦gzファイルを落として来てローカルで素直に解凍したファイルを作ってから処理に回す方が良さそうですかね?ローカルで解凍ファイルを一旦作って処理後に消すのが嫌だなと思って今回のような実装になっております。
mpyw

2021/07/21 03:36

いえ,window をいじるのが難易度が高いことであって,zlib.inflate ストリームを使うこと自体はそこまで込み入ったことでもないと思います!動作検証出来ていたらいいと思いますよ。 基本的にデフォルト値でよくて,操作する必要があるのは圧縮レベルぐらいじゃないですかね?
muteki_gamer

2021/07/21 05:16

なるほど!PHPのドキュメントにparamの詳細な説明がなかったので、デフォルトでいいかくらいの感覚でやっていました。windowの値だけはネットで32を指定している物があったので参考にした感じです。 levelとwindowの値少しいじって見てどう変わるか試してみます。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問