開発情報・ナレッジ

投稿者: SPIRERS ナレッジ向上チーム 2022年2月1日 (火)

ver.2 で DB に登録されているファイルをダウンロードするサンプルプログラム

SPIRAL ver.2 おいて、ver.2.18時点では、サイト側にファイルダウンロード機能が実装されていません。(※)
今回は、 DB に保存されているファイルをサイト側でダウンロード可能にするボタンを作ってみました。
ファイルをダウンロードさせる機能を作りたいと思っている方は参考にしてください。
※ 今後のアップデートで実装される可能性があります。
ver.2.19 のアップデートより、レコードリスト・レコードアイテム でファイル型フィールド対応が可能となっております。

変更・改定履歴

  • 改定

    パワーポイントのファイルがダウンロードできるよう JavaScript 修正

  • 変更

    HTML(Thymeleaf)の 箇所に PHP のコードが入っていた件、
    及び リード文を修正

  • 改定

    ダウンロードできる拡張子の追加 および、定数をPHP環境変数に変更

  • 改定

    利用方法の修正箇所の追記

前提

DB からファイルを取得し、ダウンロードボタンを作成します。
DB にファイルのフィールドを追加の上、試してみてください。

サンプルプログラム

PHP
<?php
//------------------------------
// 設定値用モジュール
//------------------------------
define("API_URL", "https://api.spiral-platform.com/v1/");
define("API_KEY", $SPIRAL->getEnvValue(""));
define("API_ROLE",$SPIRAL->getEnvValue("")); // ロールによるAPI権限が不要の場合
define("APP_ID",$SPIRAL->getEnvValue(""));
define("DB_ID",$SPIRAL->getEnvValue(""));

//------------------------------
// ページ内処理 サンプル
//------------------------------

$commonBase = CommonBase::getInstance();

// ファイルが入っている レコード情報を取得 
// xxx は、レコードIDをセット
$data = $commonBase->apiGetCurlAction("apps/".APP_ID."/dbs/".DB_ID."/records/xxx");
	
if(array_key_exists('status', $data)){
    //APIのエラーが発生した場合、APIのレスポンスにstatusが200以外で返ってくるので、statusをチェック
    if($data["status"] != 200){
        $SPIRAL->setTHValue("error", true);
        $SPIRAL->setTHValue("errorTxt", "エラーが発生しました。");
    }
}elseif(isset($data["error"])){
    // APIの通信自体が失敗した場合
    $SPIRAL->setTHValue("error", true);
	$SPIRAL->setTHValue("errorTxt", print_r($data,true));
}else{
    //取得成功時
    // 識別名 は、ファイルレコードの識別名 
    if(isset($data["item"]["識別名"])){
		// ファイルのデータを取得(今回はファイル1つの想定)
        $imgData = $commonBase->apiGetCurlAction(str_replace(API_URL, '', $data["item"]["識別名"][0]["url"]));
        if($imgData){
            // ファイルのデータからファイル名のデータを Thymeleaf に渡す(今回はファイル1つの想定)
            $SPIRAL->setTHValue("fileName", $data["item"]["識別名"][0]["fileName"]);
            // 取得したより、base64形式データをHTMLに渡す
            $SPIRAL->setTHValue("fileData", $imgData);
			// ファイル名から拡張子を取得
			// 拡張子名は小文字で統一し、HTMLに渡す
			$SPIRAL->setTHValue("extension", substr($data["item"]["識別名"][0]["fileName"], strrpos($data["item"]["識別名"][0]["fileName"], '.') + 1));
        }else{
			// ファイルデータが取得できない場合
            $SPIRAL->setTHValue("error", true);
            $SPIRAL->setTHValue("errorTxt", "エラーが発生しました。");
        }        
    }else{
		// ファイルデータが存在しない場合
        $SPIRAL->setTHValue("error", true);
        $SPIRAL->setTHValue("errorTxt", "添付ファイルが存在しません。");
    }
}

//------------------------------
// 共通用モジュール
//------------------------------
class CommonBase
{
	/**
	 * シングルトンインスタンス
	 * @var UserManager
	 */
	protected static $singleton;

	public function __construct()
	{
		if (self::$singleton) {
			throw new Exception('must be singleton');
		}
		self::$singleton = $this;
	}

	/**
	 * シングルトンインスタンスを返す
	 * @return UserManager
	 */
	public static function getInstance()
	{
		if (!self::$singleton) {
			return new CommonBase();
		} else {
			return self::$singleton;
		}
	}

	/**
	 * ver.2 API
	 * @return Result
	 */
	function apiGetCurlAction($addUrlPass)
	{
		$header = array(
			"Authorization:Bearer " . API_KEY,
			"X-Spiral-Api-Version: 1.1",
		);
        if(API_ROLE){
			$header = array_merge($header,array("X-Spiral-App-Role: ".API_ROLE));
		}
		$curl = curl_init();
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($curl, CURLOPT_URL, API_URL . $addUrlPass);
		curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
		curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "GET");
		curl_setopt($curl, CURLOPT_TIMEOUT_MS, 10000);
		$response = curl_exec($curl);
		if(json_decode($response)){
			return json_decode($response, true);
		}else{
			return base64_encode($response);
		}       	
	}
}

利用方法

サンプルプログラムは、ページのPHPタブにセットしてください。
各設定値をセットしたPHP環境変数を設定する
define("API_KEY", $SPIRAL->getEnvValue(""));
define("API_ROLE",$SPIRAL->getEnvValue("")); // ロールによるAPI権限が不要の場合
define("APP_ID",$SPIRAL->getEnvValue(""));
define("DB_ID",$SPIRAL->getEnvValue(""));
API_KEY
6行目
アカウント管理で発行したAPIキーをセットしたPHP環境変数の変数名
API_ROLE
7行目
アプリロールの識別名をセットしたPHP環境変数の変数名
アプリロールによる権限設定をしない場合は未入力
APP_ID
8行目
更新するアプリのIDをセットしたPHP環境変数の変数名
DB_ID
9行目
更新するDBのIDをセットしたPHP環境変数の変数名

API_KEY, API_ROLE, APP_ID, DB_ID など複数ページで使用する値は、PHP環境変数への設定を推奨しております。
PHP環境変数の設定方法は、サポートサイト PHP環境変数をご確認ください。

環境変数を使用しない場合は、getEnvValue()を使用せずに下記のように直接記載してください。

define("API_KEY", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
define("API_ROLE", "XXXAppRole"); // アプリロールを使用しない場合は未入力
define("APP_ID", "XXXXX");
define("DB_ID", "XXXXX");

次に34行目から44行目に記載されている、識別名の箇所をダウンロードさせるファイルフィールドの識別名に変更いたします。
識別名変更の箇所は5つあるのでご注意ください。

    if(isset($data["item"]["識別名"])){
		// ファイルのデータを取得(今回はファイル1つの想定)
        $imgData = $commonBase->apiGetCurlAction(str_replace(API_URL, '', $data["item"]["識別名"][0]["url"]));
        if($imgData){
            // ファイルのデータからファイル名のデータを Thymeleaf に渡す(今回はファイル1つの想定)
            $SPIRAL->setTHValue("fileName", $data["item"]["識別名"][0]["fileName"]);
            // 取得したより、base64形式データをHTMLに渡す
            $SPIRAL->setTHValue("fileData", $imgData);
			// ファイル名から拡張子を取得
			// 拡張子名は小文字で統一し、HTMLに渡す
			$SPIRAL->setTHValue("extension", substr($data["item"]["識別名"][0]["fileName"], strrpos($data["item"]["識別名"][0]["fileName"], '.') + 1));

HTML(Thymeleaf)

PHP より渡された値をボタンの引数にセットします。

HTML(Thymeleaf)
<th:block th:if="${cp.result.value['error'] != true}">
    <span th:text="${cp.result.value['fileName']}"></span><br />
    <input style="width: 150px;" type="button" value="ファイルダウンロード" th:onclick="|download('${cp.result.value['fileName']}','${cp.result.value['extension']}','${cp.result.value['fileData']}')|" />
    <a style="display:none;" id="downloader" href="#" target="_blank"></a>
</th:block>
<th:block th:if="${cp.result.value['error'] == true}">
    <span>添付がありません。</span>
</th:block>
<th:block th:if="${cp.result.value['notFound'] == true}">
    <span>添付が存在しません。</span>
</th:block>

JavaScript

HTML 側から引数で受け取った値をダウンロードする処理を書きます。
拡張子によって、MINEタイプが違います。
サンプルプログラムで対応している拡張子は、下記になります。
「zip」「pdf」「jpg」「png」「pptx」「doc」「xls」「docx」「xlsx」
「ppt」「txt」「TXT」「csv」「CSV」「tsv」「TSV」「svg」「SVG」

JavaScript
// イベント処理で設定している引数と同じ数だけ、関数でも引数をセット
function download(fileName,extension,base64){

    // BOM の用意(文字化け対策)
    var mime_ctype = "application/"+extension;
    if(extension == "pdf" ){
        var blob = toBlob('data:application/' + extension + ';base64,' + base64, mime_ctype);
    }else if(extension == "zip"){
        var blob = toBlob(base64, 'application/'+ extension);
    }else if(extension == "jpg" || extension == "png" || extension == "pptx" ){
        var blob = toBlob('data:image/' + extension + ';base64,' + base64, mime_ctype);
    }else if(extension == "doc" ){
        var blob = toBlob('data:application/msword' + ';base64,' + base64, mime_ctype);
    }else if(extension == "xls" ){
        var blob = toBlob('data:application/vnd.ms-excel' + ';base64,' + base64, mime_ctype);
    }else if(extension == "docx" ){
        var blob = toBlob('data:application/vnd.openxmlformats-officedocument.wordprocessingml.document' + ';base64,' + base64, mime_ctype);
    }else if(extension == "xlsx" ){
        var blob = toBlob('data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ';base64,' + base64, mime_ctype);
    }else if(extension == "ppt" ){
        var blob = toBlob('data:application/vnd.ms-powerpoint' + ';base64,' + base64, mime_ctype);
    }else if(extension == "txt" || extension == "TXT"){
        var blob = toBlob('data:text/plain' + ';base64,' + base64, mime_ctype);
    }else if(extension == "csv" || extension == "CSV"){
        var blob = toBlob('data:text/csv' + ';base64,' + base64, mime_ctype);
    }else if(extension == "tsv" || extension == "TSV"){
        var blob = toBlob('data:text/tab-separated-values' + ';base64,' + base64, mime_ctype);
    }else if(extension == "svg" || extension == "SVG"){
        var blob = toBlob('data:image/svg+xml' + ';base64,' + base64, mime_ctype);
    }else{
        alert("ダウンロードできません。");
        return;
    }    
    
    var url = (window.URL || window.webkitURL).createObjectURL(blob);
    // IEはdownload属性が効かないので分岐
    if (window.navigator.msSaveOrOpenBlob) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.responseType = 'blob';
        xhr.onloadend = function() {
            if(xhr.status !== 200) return;
            window.navigator.msSaveBlob(xhr.response, fileName);
        }
        xhr.send();
    } else {
        var a = document.getElementById('downloader');
        a.download = fileName;
        a.href = url;
        // ダウンロードリンクをクリックする
        a.click();
    }

    return; 
}
  
function toBlob(base64,mime_ctype) {
    const bin = atob(base64.replace(/^.*,/, ''));
    const buffer = new Uint8Array(bin.length);
    for (let i = 0; i < bin.length; i++) {
      buffer[i] = bin.charCodeAt(i);
    }
    // Blobを作成
    const blob = new Blob([buffer.buffer], {
      type: mime_ctype
    });
    return blob;
}

不具合やほかのやり方がある等あれば、
下記の「コンテンツに関しての要望はこちら」からご連絡ください。

解決しない場合はこちら コンテンツに関しての
要望はこちら