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」
    
// イベント処理で設定している引数と同じ数だけ、関数でも引数をセット
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;
}
        
        不具合やほかのやり方がある等あれば、
        下記の「コンテンツに関しての要望はこちら」からご連絡ください。