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

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

ただいまの
回答率

89.12%

JavaScriptでリクエストをPOSTしてPDF形式のレスポンスをダイレクトに開く方法

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 16K+

ringoing888

score 14

前提・実現したいこと

JavaScriptでAPIにJson形式のデータをPOSTして、APIからPDF形式の
データを返してブラウザで表示させたいと思っています。ブラウザで表示させる
とき、元のリクエスを送出したページはそのままで、PDF用のページを新規で
オープンさるという仕様を想定しています。

また、可能なら一時ファイルを作成せずにそのままのレスポンスがダイレクトに
PDF表示に繋がるとベストだと思っています。

この時、JavaScriptでAPIにJson形式のデータをPOSTしつつ、新規ページを
オープンさせつつ、かつその新規ページはPDFを表示させるという方法について、
この辺りの知見に詳しい方のアドバイスを頂きたいです。

【追記】
「JSON形式のデータをPOSTする」の前段として、<form>なりの要素で可変の
値を受け取り、それをJSON形式に組み直してPOSTしたいです。試した事のところで
長くなるので削除しちゃってますが、JSONdataの中には前段でクリックと同時に
formの値をさらって、{ key : value、 key : value } に整形する処理を
入れています。

試したこと

Jqueryで普通に$.ajaxとかでリクエストするので今色々試してみてます。

$.ajax({ 
            type : 'post', url : '/hoge',
            data : JSON.stringify(JSONdata),
            contentType: 'application/JSON',
            dataType : 'JSON',
            scriptCharset: 'utf-8',
            ↓これだとダメなのは解ってるけどどうすれば良いかが解りませんでした。
              ※ href.locationを使って、一時ファイルをどこかに作成し、リンク先に
                  遷移させるとかはやりたくない。
            success : function(data) {
                $("#response").html(JSON.stringify(data));
            },
            error : function(data) {
                $("#response").html(JSON.stringify(data));
            }
});

補足情報(言語/FW/ツール等のバージョンなど)

想定している環境は以下です。
・Python 2.7.10
・libharu (PDF生成ライブラリ)

【本番】
・PDF生成のAPIをAWSのLambdaで提供
・JSONでデータを与えるとそのデータを差し込んだPDFを返す

【開発環境】
・API部分を簡易に実現する環境としてPythonのFalconを使用
・POSTでJSONを投げる部分はJqueryを使って投げてます。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • nezume

    2016/09/09 15:10

    $.ajax を用いて生成した PDF はどのように表示されているのでしょうか?

    キャンセル

回答 4

checkベストアンサー

+1

こんな感じでどうでしょう。blob:スキームでURLを生成してます。

<!DOCTYPE html>
<html>
<head>
  <title>a</title>
  <style>
    div, embed { width: 100%; height: 125vw; }
  </style>
</head>
<body>
  <button id="b1">view pdf</button>
  <div id="d1"></div>
  <script>
  (function() {
    document.getElementById("b1").addEventListener("click", function() {
      // ブロックされないようにclickハンドラ内でオープンしとく
      var previewWindow = window.open("", "preview");
      var xhr = new XMLHttpRequest();
      xhr.onload = function() {
        var url = URL.createObjectURL(this.response);
        // オープンしといた別Windowで表示
        previewWindow.location.href = url;
        // 埋め込んじゃうというのも
        document.getElementById("d1").innerHTML =
        '<embed type="application/pdf" src="' + url + '"></embed>';
      };
      // JSON形式でポストして結果はblobで受け取る
      xhr.open("POST", "my.pdf");
      xhr.responseType = "blob";
      xhr.setRequestHeader('Content-Type', 'application/json');
      xhr.send('{"hello":"world"}');
    });
  })();
  </script>
</body>
</html>

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/09 16:26

    仕様なのかバグなのかはわかりませんがMicrosoft製のブラウザ(IE11, Edge)では動作しませんね。それらのブラウザへの対応は無理そうです。

    キャンセル

+1

pdfファイルはサイト上においておいてファイルの所在地が返ってくるんじゃなくて
pdfのバイナリーデータが返ってくるんですよね?
であれば単純にtargetをブランクで対象ファイルにpostすればいいのでは?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/08 16:50 編集

    ちょっと、説明が不足してました。まず、JSONでデータを渡すというのはハードコーディングされたものを渡すのではなく、例えばformに入ってる値とかからJSON形式に変換したいんです。

    多分、POSTするって以下の様なやり方なのかなと思いますが。。
    <a href="javascript:void(0)" onclick="document.test.submit();
    return false;" target=_blank>リンク名</a>

    <form name="test" method="post" action="/hoge">
    <input type="hidden" name="value1" value="test1">
    <input type="hidden" name="value2" value="test2">
    </form>

    ご提案の方法で確かにそうかもと思ってちょっと考えてみましたが、JSON形式に変換してPOSTっていう処理をどこかで入れないとなんですが、何か方法ありますか?まー、POSTで受けて受け側でPOSTを分解したりとかっていう方法でも実現はできると思うんですが、それってRESTのAPIとしてどうなんだろうという気がしていて。

    キャンセル

  • 2016/09/09 09:58

    単純に

    <form name="test" method="post" action="/hoge" target="_blank">
    ですね。JSON形式のPOSTという言葉に勘違いされているように見えますが
    所詮POSTされるのは生データですから
    元データをjsonで持っているということであればforで展開して
    hiddenに展開してからサブミットすればいいでしょう。

    キャンセル

  • 2016/09/09 10:08

    ご回答有り難うございます。
    別の方からの回答への返答にも有りますが、あくまでやりたい事が「JSONリクエストを送出」するという事が重要なので、hiddenに仕込む方法だとやりたい事が実現できないんですよね。

    ご存知のように、jsonリクエストをサーバーで受け取ると、リクエストボディに、
    { value : hogehoge, value2 : hogehoge }

    と入って、サーバーに送られてきます。
    form要素でpostすると、サーバー側で受け取った時
    data=hogehoge

    という形式で、リクエストボディにパラメータ名(?言い方あってるか不明ですが)=値となって送られてきます。

    JSONのリクエストにこだわっているのは、AWSのLambdaはJSONリクエストを純粋に受け付ける仕様のはずなので、できないことはないと思うけど都合が悪そうっていう事と、どうしても前段でパースしてデータ整形する処理が必要になりそうで、それだと不格好なのでなにか良いほうがないかをこちらで聞いてみたという経緯です。

    キャンセル

0

form要素にtarget属性を付けるとかでしょうか。

【HTMLタグ/フォームタグ/結果が表示されるウィンドウを指定する - TAG index Webサイト】
http://www.tagindex.com/html_tag/form/form_target.html

【フォームから別ウィンドウを開く方法のまとめ: 小粋空間】
http://www.koikikukan.com/archives/2010/12/20-005555.php

POST前にJSON化せずに受け取った変数をPython側でJSONにするのが手早い気がしますが、submitイベントを拾って書き換える事も可能です。

【【jQuery】submit前に処理を行う方法 - Qiita】
http://qiita.com/kazu56/items/cdbf4e371cdc699709f1

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/09 04:37

    すいません、「JSONリクエスト」を勘違いしていました。レスポンスボディがJSONなんですね。それであればAjaxで投げる以外に方法は無いと思います。submitイベント時にフォームから値を取り出してJSON化して、質問文に書かれているようなコードで送信、PDFがURLで返ってくるならそこに location.href で移動ですかね。バイナリで返ってくるならjQueryではちょっと難しいかもしれません。

    【kintoneで外部APIからPDF帳票をダウンロードする - Qiita】
    http://qiita.com/yukinagae/items/9e64d8ad93c65584bee9#_reference-a212c6972da950562fca



    キャンセル

  • 2016/09/09 04:43

    夜分遅く迄ご丁寧に有難うございます。
    あーやはりそうですか。。

    やはり一旦テンポラリにPDFファイルを生成して、そのリンクを開くみたいな方法しかないんですかね。。ちょっと他に良い方法がないか、別の方法も検討してみようと思います。

    ご丁寧に有難うございました。

    キャンセル

  • 2016/09/09 14:16

    > 一旦テンポラリにPDFファイルを生成して、そのリンクを開くみたいな方法しかない

    バイナリを(サーバ上でテンポラリを作らず)に開くことは出来ます。

    【JavaScriptでファイルダウンロード処理を実現する - Qiita】
    http://qiita.com/wadahiro/items/eb50ac6bbe2e18cf8813

    キャンセル

0

私の今やっているwebサービスで、半年前ほどに実装しました(私は指示だけで実際に実装したのは他のメンバですが…)

APIはレスポンスヘッダをContent-Type:application/pdfで返し、
レスポンス本文にpdfのバイナリをそのまま渡します

フロントエンドでは、ダウンロードみたいなボタンがaタグになってまして、
aタグのhrefに上記のAPI(pdfバイナリを返す)を指定し、targetは_blankとしています

で、ダウンロードボタンを押すと、別ウインドウでpdfが開くようになってます

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/09 13:47 編集

    すいません。リクエストとレスポンスが若干言葉の使い方統一されてなくて(←というか間違って使っていて)混乱させてしまってる所あるかもしれませんね。。

    もちろんリクエストとレスポンスの話は理解しています。話はもう少しややこしい話でして、リクエスト送出時にJSON形式でデータを送りたいのです。で、リクエストBodyにjson形式でデータを埋め込んで、サーバーにリクエストを送りたいのですが、これはもちろん$.ajaxでdataにjson形式指定すれば送れます。

    ただ、それだと非同期通信になってしまい新規ウィンドウを開くという操作を指定できない(ような気がする。調べた限りそういうオプションとか無い。$.ajaxには)というのが問題です。

    新規ウィンドウを立ち上げる所にフォーカスすると、既出の_targetとか使えば出来そうですが、submit使うとリクエストBodyにJSON形式でデータを仕込むって方法がなく、
    $.ajax使うとJSON形式のリクエストは送出出来るけど新規ウィンドウを開いてPDFを表示させるというのが出来ないって言う所で、方法を模索しています。

    キャンセル

  • 2016/09/09 13:56

    私どもの実装ですと、
    サーバはバックエンドで定期的にpdfファイルを作成していて、サーバはpdfファイルを大量に保持しています。
    ユーザが画面UIから日時などを設定してダウンロードボタンを押すと、
    画面UIで選択されている日時などを取得し、動的にjsonパラメータを作成し、APIを呼び出します。
    サーバ側は渡されたjsonパラメータを見て、大量のpdfファイルの中から一致するpdfファイルのバイナリをフロントエンドへ返します
    残念ながら、APIの中で動的にpdf作成はしていないです
    実装はjavaのサーブレットです

    ちなみにバックエンドでは、pdf作成用のwebページを持っていまして、
    wkhtmltopdfというツールを利用してpdf作成しています
    参考になるかわかりませんが…

    動的なpdf作成APIの実装というのも技術的には可能と思います
    pdf作成処理が重くなければですが

    キャンセル

  • 2016/09/09 13:58

    同期非同期の件だけですと、
    $.ajax({
    async: false,
    })
    でいけるかと思います

    キャンセル

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

  • ただいまの回答率 89.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる