web-dev-qa-db-ja.com

ダウンロード済みPDFデータが含まれていますが、空に見えます

JavaScriptでPDFファイルダウンロード機能を実装しようとしています。
POSTリクエストへの応答として、PDFファイル、Chrome DevToolsコンソールで( oResultデータコンテナ、フラグメント):

"%PDF-1.4↵%����↵40obj↵<>stream↵x��

今、私はダウンロードプロセスを初期化しようとしています:

let blob = new Blob([oResult], {type: "application/pdf"});

let link = document.createElement('a');

link.href = window.URL.createObjectURL(blob);
link.download = "tstPDF";

link.click();

その結果、ボタンをクリックするとtstPDF.pdfが表示され、正しいページ数が含まれていますが、PDF自体は空であり、コンテンツはありません。 6KBですが表示されます。

PDFを生成するJavaサーバーサイドモジュールをテストすると、すべてが正常に機能し、InputStreamからServletOutputStreamを送信します。したがって、問題はクライアント側のどこかにあり、おそらくMIMEBLOBencodingなどの問題です。

生成されたPDFにデータが表示されない理由がわかりますか?

4
Mike B.

私は問題を解決しました。問題は、データがサーバーからクライアントに配信される方法にありました。サーバーがデータをBase64エンコーディングで送信することを保証することが重要です。そうしないと、クライアント側はPDF文字列をバイナリ形式に逆シリアル化できません。以下を参照してください。完全なソリューション。

サーバー側:

OutputStream pdfStream = PDFGenerator.pdfGenerate(data);

String pdfFileName = "test_pdf";

// represent PDF as byteArray for further serialization
byte[] byteArray = ((Java.io.ByteArrayOutputStream) pdfStream).toByteArray();

// serialize PDF to Base64
byte[] encodedBytes = Java.util.Base64.getEncoder().encode(byteArray);

response.reset();
response.addHeader("Pragma", "public");
response.addHeader("Cache-Control", "max-age=0");
response.setHeader("Content-disposition", "attachment;filename=" + pdfFileName);
response.setContentType("application/pdf");

// avoid "byte shaving" by specifying precise length of transferred data
response.setContentLength(encodedBytes.length);

// send to output stream
ServletOutputStream servletOutputStream = response.getOutputStream();

servletOutputStream.write(encodedBytes);
servletOutputStream.flush();
servletOutputStream.close();

クライアント側:

let binaryString = window.atob(data);

let binaryLen = binaryString.length;

let bytes = new Uint8Array(binaryLen);

for (let i = 0; i < binaryLen; i++) {
    let ascii = binaryString.charCodeAt(i);
    bytes[i] = ascii;
}

let blob = new Blob([bytes], {type: "application/pdf"});

let link = document.createElement('a');

link.href = window.URL.createObjectURL(blob);
link.download = pdfFileName;

link.click();

参照トピック:

9
Mike B.

これに感謝します。それは実際に動作します。

ところで、これが私がスプリングコントローラーとjasperによって生成されたpdfでajaxを使用してそれを行う方法です

コントローラー:

public ResponseEntity<?> printPreview(@ModelAttribute("claim") Claim claim)
{
    try
    {
        //Code to get the byte[] from jasper report.
        ReportSource source = new ReportSource(claim);
        byte[] report = reportingService.exportToByteArrayOutputStream(source);

        //Conversion of bytes to Base64
        byte[] encodedBytes = Java.util.Base64.getEncoder().encode(report);

        //Setting Headers
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/pdf"));
        headers.setContentDispositionFormData("pdfFileName.pdf", "pdfFileName.pdf");
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
        headers.setContentLength(encodedBytes.length);

        return new ResponseEntity<>(encodedBytes, headers, HttpStatus.OK);
    }
    catch (Exception e)
    {
        LOG.error("Error on generating report", e);
        return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
    }
 }

Ajax:

    $.ajax({
       type: "POST",
       url: "",
       data: form.serialize(), //Data from my form
       success: function(response)
       {
                let binaryString = window.atob(response);
                let binaryLen = binaryString.length;
                let bytes = new Uint8Array(binaryLen);

                for (let i = 0; i < binaryLen; i++) {
                    let ascii = binaryString.charCodeAt(i);
                    bytes[i] = ascii;
                }

                let blob = new Blob([bytes], {type: "application/pdf"});
                let link = URL.createObjectURL(blob);
                window.open(link, '_blank');
       },
       error: function()
       {

       }
     });

これにより、PDFが新しいウィンドウに読み込まれます。

参照: Spring MVCを使用して生成されたPDFを返す

1
jeloVilla