21
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

kintoneで外部APIからPDF帳票をダウンロードする

Last updated at Posted at 2016-05-05

kintoneで外部APIからPDF帳票をダウンロードする

前提

やりたいこと

kintone内にあるデータを外部API(※PDF帳票作成用API)に投げて、PDFファイルをダウンロードしたい

想定するユースケース

  • 既存システムからkintoneにデータを移行したが、PDF帳票機能(API)が既存システム側にある
  • なんらかの事情でkintoneプラグインのプリントクリエイターを使えないため、新規に外部APIを作成する

構成

構成は以下のような感じです。

kintonePDF出力の構成例

外部API側

上記の構成の通り、JavaScriptでクロスドメイン通信することになるので、外部API側でクロスドメイン通信を許可してあげる必要があります。

例えば外部API側でapacheを使用している場合には、以下のようにAccess-Control-Allow-OriginでkintoneのURLを指定して許可します。

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "[kintoneのURL (例 https://hoge123.cybozu.com)]"
    Header add Access-Control-Allow-Headers "content-type"
    Header add Access-Control-Expose-Headers "Content-Disposition"
    Header add Access-Control-Allow-Methods "POST"
</IfModule>

クロスドメイン通信について、詳しくは以下のドキュメントを読んでください。
参照:
HTTP アクセス制御 (CORS)
Ajaxブラウザセキュリティ - 主戦場をDOMに移したXSS

完成コード(kintone側)

以下2つのJavaScriptファイルをkintone上にアップロードしたら完了です。
kintone上に「PDF出力」というボタンが表示されているはずです。

PDFボタン表示

  • export_pdf.js : PDFファイルを出力する共通関数
  • sample_form_download.js : 実際にkintone上にPDF出力ボタンを設置してPDFファイルダウンロードするプログラム
export_pdf.js
/*
PDFファイルを出力する関数
(jQueryだとバイナリファイルのダウンロードが上手くいかないので、仕方なくXMLHttpRequestを使用している)
*/
function export_pdf(url, post_data) {

    // aタグを作成するのがコツみたい。後のコードでここで作成したaタグをクリックする。
    // 参照: http://stackoverflow.com/questions/19327749/javascript-blob-filename-without-link
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";

    // バイナリファイルをJavaScriptでダウンロードする方法
    // 参照: http://www.html5rocks.com/en/tutorials/file/xhr2/
    window.URL = window.URL || window.webkitURL;

    var xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
    xhr.responseType = 'arraybuffer'; // バイナリで読み込むようにレスポンスタイプを指定(重要!)

    xhr.onload = function(e) {
        if (this.status == 200) { // レスポンスステータスが200(成功)の時だけ実行する

            // ファイル名をレスポンスヘッダのContent-Dispositionから取得しているだけ。
            // Content-Disposition: attachment; filename=[ファイル名]
            // arraybufferで指定しているためか、以下のように頑張って取得するしかないみたい。。。
            var filename = "default.pdf"; // 念のためデフォルトのファイル名を設定(※すぐ下で上書きされる)
            var disposition = this.getResponseHeader('Content-Disposition');
            if (disposition && disposition.indexOf('attachment') !== -1) {
                var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                var matches = filenameRegex.exec(disposition);
                if (matches != null && matches[1]) {
                    filename = matches[1].replace(/['"]/g, ''); // ファイル名があったら上書きする
                }
            }

            // PDFファイルをダウンロードする
            var response_data = this.response;
            var file = new Blob([response_data], {type: 'application/pdf'});
            var url = URL.createObjectURL(file);
            a.href = url;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(url); // 別タブで開いてPDFファイルを表示する
        }
    };

    xhr.send(JSON.stringify(post_data)); // POSTする前に、投げるデータをstringifyして文字列として渡すこと
}
sample_form_download.js
/*
kintone内のデータを外部APIに投げてPDFファイルをダウンロードするプログラム
*/
(function () {
    "use strict";

    // 詳細画面を表示した際にイベント発火する
    // ※別に一覧画面にイベントを設置してもOK。
    kintone.events.on('app.record.detail.show', function(event) {

        // PDF出力用のボタンを作成
        var button = document.createElement('button');
        button.id = 'pdf_button'; // IDは何でもいいです
        button.innerHTML = 'PDF出力'; // お好きなボタンの表示名
        button.onclick = function() { // PDF出力ボタンをクリックするとPDFファイルをダウンロード

            // 詳細画面で表示しているレコード情報を取得
            // ※ここでデータをごにょごにょして、外部APIに投げる用のデータを作成する感じです
            var data = kintone.app.record.get().record;
            
            export_pdf('https://[外部APIのURLもしくはIPアドレス]', data); // PDFファイル出力(※export_pdf.jsの関数を呼ぶ)
        };

        // PDF出力用ボタンをkintone上のヘッダの箇所に表示する
        // ※基本的にヘッダ部分がスペースとして空いているため。
        kintone.app.record.getHeaderMenuSpaceElement().appendChild(button);
    });
})();

Q&A

Q1. なぜjQueryのajaxじゃなくてXMLHttpRequest使ってるの?
A1. PDFファイルをバイナリとしてダウンロードする場合に、jQueryだとうまくいかないので。。。汗
参照: バイナリファイルをAjaxで取得する際に注意する点

Q2. 外部API側でPDFファイルを作成して、そのファイルパスを渡してダウンロードした方が楽じゃない?
**A2. そっちの方が楽ですけど、外部API側でファイルを作りたくないです。**理由として、
①セキュリティ的にファイルのリンクを公開したくない
②外部API側の容量を圧迫する可能性があるので作成したファイルを消す必要がある(面倒くさい)
③古いファイルのリンクが残っているとkintone側のデータと作成済みのファイルの内容がズレる可能性がある(なんか色々面倒くさい)

ためです。

21
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?