ページを PDF として印刷しようとするとヘッドレス Chrome がハングする

ページを PDF として印刷しようとするとヘッドレス Chrome がハングする

私たちの組織では、Web ページを PDF に変換するために headless-chrome を使用しています。そのためには、Chrome インスタンスを起動して通信するために次の CDT クライアントを使用する特別な Java アプリケーションがあります。https://github.com/kklisura/chrome-devtools-java-client ほとんどの場合、すべてが期待どおりに動作し、PDF を取得できますが、一部の Web ページでは、PDF 印刷手順中に headless-chrome がハングします。このようなページの例:

https://www.idc.com/cee/events/64662-web-developers-event-for-automatic-tests-on-idc-com-do-not-update-manually/print-agenda

ここで指定されている必要な手順をすべて実行したにもかかわらず、Chrome のヘッドレス インスタンスから有用なログを取得できませんでした。https://www.chromium.org/for-testers/enable-logging --remote-debugging-port オプションを使用してこれらのヘッドレス インスタンスをデバッグしようとしましたが、コンソールやその他の場所で疑わしいものは見つかりませんでした。ページは正常に読み込まれたようで、Chrome がページの印刷を拒否しただけのようです。

他にも同様の問題を抱えている人はいますか? あるいは、なぜそれが起こるのか誰か知っている人はいませんか? Chrome のヘッドレス インスタンスでログを有効にする方法についてヒントを持っている人はいませんか? どのような助けでもいただければ幸いです。

アプリケーションのバージョンは次のとおりです。

Google Chrome: 76.0.3809.100
chrome-devtools-java-client: 1.3.5

ありがとう!マックス。

答え1

この問題は、基盤となるWebSocket API実装であるTyrusによって発生します。デフォルトの最大メッセージサイズは4MBこのURLや他の多くのURL(特に背景の印刷を有効にした場合)では、PDF(Base64でエンコードした場合)が4MBの制限を超えてしまいます。この場合、WebSocketはバッファオーバーフローエラーしかし、私たちはそれをchrome-devtools-java-client WebsocketのonCloseイベントをリッスンしない

代わりに印刷結果をストリーミングすることでこの問題を解決できます...

をパラメータPrintToPDFTransferMode.RETURN_AS_STREAMとして渡し、4MB の制限より大幅に少ないバッファ (Base 64 による増加後でも) でストリームから読み取ります。私は 1MB を使用します:transferModePage.printToPdf

private static final int READ_BUFFER_SIZE = 1048576;
final PrintToPDF printToPDF = page.printToPDF(..., PrintToPDFTransferMode.RETURN_AS_STREAM);

final IO io = devToolsService.getIO();
int offset = 0;
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
    do {
        final Read read = io.read(printToPDF.getStream(), offset, READ_BUFFER_SIZE);
        if (read.getBase64Encoded() == Boolean.TRUE) {
            byte[] decode = Base64.getDecoder().decode(read.getData());
            offset += decode.length;
            fos.write(decode);
        } else {
            byte[] decode = read.getData().getBytes(StandardCharsets.UTF_8);
            offset += decode.length;
            fos.write(decode);
        }

        if (read.getEof() == Boolean.TRUE) {
            break;
        }
    } while (true);
}
io.close(printToPDF.getStream());

offset結果が Base 64 でエンコードされていない場合の処理​​についてはよくわかりませんが、印刷時にそれが表示されることはないと思います。

関連情報