今回のアップデート(2016/05/08 の定期メンテナンスにおけるkintone API、User API更新情報)で難解なのがこのkintone.proxy.upload()です。kintone JavaScript APIとして外部APIをコールできるkintone.proxy()のファイルアップロード版なのですが、試行錯誤の結果、「transfer.sh」というファイル共有サービスで利用可能なことが分かったのでご紹介したいと思います。
transfer.sh
transfer.sh(http://transfer.sh)は、コマンド・シェルでアップロードできて、10GBまでのファイルが14日間ストアされるファイル共有サービスです。公式サイトを見ると売り文句の通り、cURLコマンドやシェルの実行例が記載されています。

次のようなコマンドでローカルファイル(logo.jpg)をアップロード出来ます。メソッドとしてはPUTで実行されています。
$ curl --upload-file ./logo.jpg https://transfer.sh/logo.jpg # リクエスト https://transfer.sh/66nb8/logo.jpg # レスポンス
レスポンスとして、共有されたファイルのURLが発行されます。
kintone.proxy.upload() による添付ファイルの共有
kintone.proxy.upload()を使えば、このcURLコマンド相当の処理をkintone JavaScript APIで実装し、kintoneに添付されたファイルをオープンに共有することが簡単にできるというわけです。早速設定していきましょう。
アプリ
まず、次のようなフィールドを持ったアプリを準備しましょう。
| フィールド名 | フィールドコード(フィールドタイプ) | 利用目的 |
|---|---|---|
| タイトル | Title(文字列1行) | タイトル |
| ファイル | File(ファイル) | 管理しているファイル ※今回transfer.shで共有できるファイルは最上段の1個です。 |
| (スペースフィールド) | space(スペース) | ボタン設置用スペースフィールド |
| 共有URL | FileShareUrl(リンク:Web) | transfer.shで共有されたファイルリンク |
| 共有終了日 | EndDay(日付) | ファイル共有が終了する日 |
JavaScriptのコーディング例
/*
* fileShare.js
*
* Dependencies:
* jQuery( https://js.cybozu.com/jquery/1.11.3/jquery.min.js )
* SweetAlert( https://js.cybozu.com/sweetalert/v1.1.0/sweetalert.min.js / https://js.cybozu.com/sweetalert/v1.1.0/sweetalert.css)
* spin.js( https://js.cybozu.com/spinjs/2.3.2/spin.min.js )
* Moment.js( https://js.cybozu.com/momentjs/2.10.6/moment-with-locales.min.js )
*/
jQuery.noConflict();
(function($) {
"use strict";
// kintoneの添付ファイルをBlobで取得する
function getBlob(fileKey, fileName, callback) {
var apiurl = '/k/v1/file.json?fileKey=' + fileKey;
var xhr = new XMLHttpRequest();
xhr.open('GET', apiurl, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); //これが無いとIE,FFがNG
xhr.responseType = "blob";
var blob = xhr.responseType;
xhr.onload = function() {
var blob = xhr.response;
callback(blob);
};
xhr.send();
}
// transfer.shにファイルをアップロードする
function uploadToService(fileName, blob) {
var url = 'https://transfer.sh/' + encodeURIComponent(fileName);
return kintone.proxy.upload(
url,
'PUT', {}, {
'format': 'RAW',
'value': blob
}
);
}
// transfer.shの公開URLと共有終了日をkintoneのレコードにREST APIでセットする関数
function updateRecord(url) {
url = url.replace('\n', '');
return kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', {
app: kintone.app.getId(),
id: kintone.app.record.getId(),
record: {
"FileShareUrl": {
"value": url // 共有URL
},
"EndDay": {
"value": moment().add(13, 'day').format('YYYY-MM-DD') // 共有終了日
}
}
});
}
kintone.events.on(['app.record.detail.show'], function(event) {
var record = event.record;
var attachments = record['File'].value;
// スペース要素
var el = kintone.app.record.getSpaceElement('space');
// 親要素のサイズを調整
$(el).parent().width('200px');
$(el).parent().height('100px');
$(el).css({
'margin-left': '10px'
});
// 正味のコンテナ要素
var $container = $('<div>').prop({
'id': 'container'
}).appendTo(el);
// ボタン等追加
$container.append(
$('<button>').prop({
id: 'joyzo-button'
}).css({
'margin-top': '30px'
}).addClass('kintoneplugin-button-normal').text('transfer.shで共有'),
$('<div>').text('※最上段の添付ファイルのみが共有されます').css({
'font-size': '8pt',
'margin': '3px'
})
);
// ボタンクリックイベント
$('#joyzo-button').bind('click', function() {
showSpinner();
//console.log(attachments);
// 添付ファイル(Blob)の取得 -> transfer.shへのアップロード -> レコード更新
new kintone.Promise(function(resolve, reject) {
getBlob(attachments[0].fileKey, attachments[0].name, function(blob) { // 添付ファイルの取得
resolve(blob);
});
}).then(function(blob) {
//console.log(blob);
return uploadToService(attachments[0].name, blob); // transfer.sh へのアップロード
}).then(function(resp) {
//console.log(resp);
var url = resp[0];
return updateRecord(url); // レコード更新
}).then(function() {
hideSpinner();
swal({
title: 'ファイルアップロード完了',
text: '',
type: 'success'
}, function() {
location.reload(true);
});
}).catch(function(e) {
hideSpinner();
//console.log(e);
var errmsg = 'ファイルアップロード失敗。';
if (e.message !== undefined) {
errmsg += e.message;
}
swal({
title: 'ファイルアップロード失敗',
text: errmsg,
type: 'error'
}, function() {
return;
});
});
});
function showSpinner() {
// 要素作成等初期化処理
if ($('.kintone-spinner').length == 0) {
// スピナー設置用要素と背景要素の作成
var spin_div = $('<div id ="kintone-spin" class="kintone-spinner"></div>');
var spin_bg_div = $('<div id ="kintone-spin-bg" class="kintone-spinner"></div>');
// スピナー用要素をbodyにappend
$(document.body).append(spin_div, spin_bg_div);
// スピナー動作に伴うスタイル設定
$(spin_div).css({
'position': 'fixed',
'top': '50%',
'left': '50%',
'z-index': '510',
'background-color': '#fff',
'padding': '26px',
'-moz-border-radius': '4px',
'-webkit-border-radius': '4px',
'border-radius': '4px'
});
$(spin_bg_div).css({
'position': 'absolute',
'top': '0px',
'z-index': '500',
'width': '150%',
'height': '150%',
'background-color': '#000',
'opacity': '0.5',
'filter': 'alpha(opacity=50)',
'-ms-filter': "alpha(opacity=50)"
});
// スピナーに対するオプション設定
var opts = {
'color': '#000'
};
// スピナーを作動
new Spinner(opts).spin(document.getElementById('kintone-spin'));
}
// スピナー始動(表示)
$('.kintone-spinner').show();
};
// スピナーを停止させる関数
function hideSpinner() {
// スピナー停止(非表示)
$('.kintone-spinner').hide();
};
return event;
});
})(jQuery);
コードの内容は、「transfer.shで共有」ボタンのクリックで、
①添付ファイルフィールドの1つ目の添付ファイルをXMLHttpRequestを用いてBlob形式で取得(getBlob関数)
②kintone.proxy.upload()でtransfer.shにリクエスト(uploadToService関数)
③kintone.proxy.upload()のリクエストが成功し、transfer.shでファイルが公開できたら、アクセスURLと共有終了日をレコード更新でセットする(updateRecord関数)
という感じの内容です。
アプリの動作とファイル共有
①レコード新規登録・編集画面でファイルをレコードに添付し、保存後に②「transfer.sh」ボタンをクリック

③リクエスト(アップロード)が成功すると、(ダイアログ表示後リロードされて)共有URLがセットされれる

④共有URLにアクセスすると、ファイルが共有され、ダウンロード出来る状態になっていることが確認できる。第3者に共有したい場合にもこの共有URLを伝えることで、アクセスできる

kintone.proxy.upload()の考察
今回この記事でわかることをひとつまとめておくと、『cURLコマンドにおける「–upload-file」オプションでのリクエストとkintone.proxy.upload()/PUTに等価性がある』ということです。
まとめ
kintoneに添付されたファイルをパブリックな場に共有したい際に便利なtransfer.shへの連携を例にkintone.proxy.upload()のリクエスト例をお届けしました。冒頭に「試行錯誤した結果」と書かせて頂きましたが、実はこの記事は色々調べてみた結果から辿ってまとめたものです。この経緯を詳しく知りたい方は長編版をまとめていますので、こちらを御覧ください。
