LWCでのファイルダウンロード

# 目的
LWCでのファイルダウンロード方法を共有します。

# ソース構成図
```
force-app
  └─main
      └─default
          ├─classes
          │  ├─GetFileDemoController
          │  └─GetFileDemoController.cls-meta.xml
          └─lwc
            　└─fileDownload
```

##### class:GetFileDemoController
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1767576/f0af0456-2141-5357-3541-f9a8f597a713.png)

```GetFileDemoController.cls
public with sharing class GetFileDemoController {

    /**
     * ファイル一覧を取得
     */
    @AuraEnabled
    public static List<ContentVersion> getFileList(){
        try {
            List<ContentVersion> contentVersions = [SELECT
                                                        Id, 
                                                        Title,
                                                        FileExtension,
                                                        ContentSize
                                                    FROM
                                                        ContentVersion 
                                                    ];
            if(contentVersions.size() > 0)
                return contentVersions;
            return null;
        } catch (Exception e) {
            throw new AuraHandledException(e.getMessage());
        }
    }
}
```
```GetFileDemoController.cls-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <status>Active</status>
</ApexClass>
```

##### LWC:fileDownload
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1767576/2463ee04-75ec-0bfc-a13f-af7f5979d8b5.png)

```fileDownload.html
<template>
    <template if:true={loading}>
        <lightning-spinner alternative-text="Loading" size="medium"></lightning-spinner>
    </template>
    <lightning-card>
        <div slot="footer">
            <lightning-datatable hide-checkbox-column key-field="Id" columns={columns} data={datas} onrowaction={handleRowAction}>
            </lightning-datatable>
        </div>
    </lightning-card>
</template>
```
```fileDownload.js
import { LightningElement, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import getFileList from '@salesforce/apex/GetFileDemoController.getFileList';

export default class FileZipDemo extends LightningElement {
    @track columns;
    @track datas;
    @track loading;

    /**
     * メッセージ表示
     * @param {window} that 
     * @param {string} title タイトール
     * @param {string} message メッセージ
     * @param {string} variant タイプ info、success、warning、error
     */
    showToast(title, message, variant) {
        const event = new ShowToastEvent({
            title: title,
            message: message,
            variant: variant,
        });
        this.dispatchEvent(event);
    }


    /**
     * ファイルサイズ変換
     * @param {*} size バイト
     * @returns 変換後のサイズ
     */
    fileSizeUnit(size) {
        // 1 KB = 1024 Byte
        const kb = 1024
        const mb = Math.pow(kb, 2)
        const gb = Math.pow(kb, 3)
        const tb = Math.pow(kb, 4)
        const pb = Math.pow(kb, 5)
        const round = (size, unit) => {
            return Math.round(size / unit * 100.0) / 100.0
        }

        if (size >= pb) {
            return round(size, pb) + 'PB'
        } else if (size >= tb) {
            return round(size, tb) + 'TB'
        } else if (size >= gb) {
            return round(size, gb) + 'GB'
        } else if (size >= mb) {
            return round(size, mb) + 'MB'
        } else if (size >= kb) {
            return round(size, kb) + 'KB'
        }
        return size + 'バイト'
    }

    /**
     * デートフォマート
     * @param {Date} date date
     * @param {string} fmt format
     * @returns {string} StringDate
     */
    dateFormat(date, fmt = 'YYYY/mm/dd') {
        let ret;
        const opt = {
            'Y+': date.getFullYear().toString(), // 年
            'm+': (date.getMonth() + 1).toString(), // 月
            'd+': date.getDate().toString(), // 日
            'H+': date.getHours().toString(), // 時
            'M+': date.getMinutes().toString(), // 分
            'S+': date.getSeconds().toString() // 秒
        };
        for (let k in opt) {
            ret = new RegExp('(' + k + ')').exec(fmt);
            if (ret) {
                fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, '0')))
            };
        };
        return fmt;
    }

    /**
     * ファイルダウンロード 
     */
    async downloadFile(Id) {
        try {
            this.loading = true;
            window.location.href = `/sfc/servlet.shepherd/version/download/${Id}`
        } catch (err) {
            console.error("Error: " + err);
            this.showToast('', err.body.message, 'error');
        } finally {
            this.loading = false
        }
    }

    /**
     * RowAction
     * @param {*} event 
     */
    async handleRowAction(event) {
        const action = event.detail.action;
        const row = event.detail.row;
        switch (action.name) {
            case 'download':
                await this.downloadFile(row.Id);
                break;
            default:
                break;
        }
    }

    /**
     * 初期化
     */
    async connectedCallback() {
        this.columns = [
            { label: 'Id', fieldName: 'Id', },
            { label: 'ファイル名', fieldName: 'Title' },
            { label: '拡張子', fieldName: 'FileExtension' },
            { label: 'ファイルサイズ', fieldName: 'ContentSize' },
            {
                type: 'action',
                typeAttributes: {
                    rowActions: [
                        { label: 'ダウンロード', name: 'download' },
                    ],
                    menuAlignment: 'auto'
                }
            }
        ];
        this.datas = await getFileList();
        this.datas = this.datas.map((e, i) => {
            return {...e, ContentSize: this.fileSizeUnit(e.ContentSize) }
        })
    }
}
```
```fileDownload.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>
```

# Salesforce側動作確認

### Salesforce側Lightning コンポーネントタブを作成
![スクリーンショット 2021-08-02 173914.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1767576/e6c6b4b7-8f9a-b136-0b76-56c87ebbd2b5.png)

### タブを開いて、ファイルダウンロードする
![スクリーンショット 2021-08-02 174443.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1767576/8c77a308-595f-20f6-c332-eb57a51289be.png)


![スクリーンショット 2021-08-02 174727.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1767576/0092df96-190d-89d0-ded1-adb8354de7ec.png)

# 参考

https://salesforce.stackexchange.com/questions/203030/content-document-download-url-params

https://cafeforce.com/mass-attachments-documents-download-salesforce-lwc/