---
# System prepended metadata

title: Upload file to server
tags: [GCS]

---

---
title: Upload file to server
tags: GCS
---

# Upload file to server

## 需求
- project cover 需要上傳圖片，並且要能將上傳的圖片顯示在頁面上

## 討論

1. 目前 tms 是透過套件 multiparty 來實現上傳功能，沒有一隻 API 專門處理，是否需要另開 API 上傳檔案
    - 好處: 如未來又有需要上傳檔案，可減少重複的 code
    - 未來變更上傳套件，只需要改一隻 API 即可
> 未來有什麼需要上傳檔案？
> 按照目前的設計好像沒有了


2. 是否需要另外在 google cloud storage 儲存檔案，或是另外開一台 disk 儲存
    - GCS 好處: 資料自動具有多份的備份，具有完整的資料保護機制

## k8s 現況
![](https://i.imgur.com/3CEIzQ9.jpg)
- 目前只有透過 NFS(Network File System) 做 pm2 log 的儲存 [可以參考這裡](https://gitlab.com/fio.io/tms/-/blob/dev/scripts/install-pm2-logrotate.sh)
- 如果需要儲存其他東西，可能需要再加開一顆硬碟，透過 NFS 來存取
 
## 成本效能比較

- 實作成本（錢 and 時間）
    - cloud sorage 比較便宜 （GCS 勝
    - 有 SDK 已經串好，可以馬上套用 VS 新開一個 disk 透過 NFS 儲存 實作時間不明 (GCS 勝？
- 效能
    - 上傳到 tms 再上傳到 GCS vs 上傳到 tms 結束 ( disk 勝 
    - 顯示圖片可以透過公開 GCS 路徑  vs 直接顯示 ( disk 險勝
### disk
![](https://i.imgur.com/76kUIBO.png)

![](https://i.imgur.com/rlCbSCY.png)

### cloud storage
![](https://i.imgur.com/dq58z3T.png)

![](https://i.imgur.com/4m485P3.png)
https://cloud.google.com/storage/pricing?hl=zh-tw


## 研究筆記
- GCS and AWS s3
    - GCS and AWS s3 皆有 bucket, 概念也幾乎一樣
    - 在試做的 opencert 裡將 bucket 分成 `fio-service-dev` / `fio-service-prod`
    - 定義公開的路徑 `https://storage.cloud.google.com/${BUCKET_NAME}/${OBJECT_NAME}`
> GCS: Cloud Storage 使用平面命名空間運行，這意味著文件夾實際上不會存在於Cloud Storage 中。
> 如果您在存儲桶 your-bucket 中創建名為 folder1/file.txt 的對象，則該對象的路徑為your-bucket/folder1/file.txt，但沒有名為 folder1 的文件夾；字符串 folder1 是對象名稱的一部分。
> 
> AWS: 概念與 GCS 一樣 , Amazon S3 supports buckets and objects, and there is no hierarchy. However, by using prefixes and delimiters in an object key name, 
> the Amazon S3 console and the AWS SDKs can infer hierarchy and introduce the concept of folders.
> [AWS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html)

![](https://i.imgur.com/Wmk11Vv.png)
- Google 建議如果是給不同人看，推薦將 bucket 分開
- 可能會將 bucket 分成: 公開的 bucket 與 不公開的 bucket
- 如果需要將資料切換是否公開，可以用 copy or move 的功能，將檔案從公開的 bucket 轉到不公開的 bucket 
    - GCS 與 AWS S3 都有此功能
    - 切換至不同儲存位置可能會有額外頻寬的費用 e.g. 亞洲 => 歐洲
[GCS copy](https://googleapis.dev/nodejs/storage/latest/File.html#copy)
[GCS move](https://cloud.google.com/storage/docs/copying-renaming-moving-objects#storage-move-object-nodejs)

https://cloud.google.com/storage/docs/access-control/?hl=zh-tw&_ga=2.204249872.-1238561395.1660620188&_gac=1.116408692.1663053632.CjwKCAjw1ICZBhAzEiwAFfvFhK5aGUUGi8xJQIUS-XIC9MB0-EBC5VX5xE4Zd_bnSa2baYCxtoXCpBoCVKcQAvD_BwE

## 遇到的問題
1. 權限設定: bucket 可以統一設定公開與不公開 vs 不設統一權限，單一檔案設定公開與不公開
    - 單一檔案設定 public 權限
        - 好處: 不用分過多的 bucket
        - 壞處: 每個檔案不同權限，較難管理，不易看出哪些是公開，哪些是不公開，上傳時需要多 call 一個 function
    - 由 bucket 統一設定 public 權限
        - 好處: 單一 bucket 就是決定 public or private 很直覺
        - 壞處: 依照每個環境需要多開兩個 bucket，數量會比較多（預計會多兩個 bucket 而已

#### 結論: 使用 bucket 統一設定權限， bucket 過多並不影響效能，GCS 也沒有限制 bucket，方便管理比較重要

2. folder 依照功能來分類資料 vs 簡單分類 分成跟 project 相不相關
    - 依照功能來分類資料
        - 好處:
            - 想要轉移特定功能的資料時可以透過整個 folder 一起轉移
        - 壞處:
            - 需要多定義 folder 如何命名 e.g. /nft
    - 依照跟 project 相不相關分類資料
        - 好處:
            - 只需要知道跟 project 相不相關即可上傳到對應的 folder

> FiO 有很多功能會要保存資料嗎? 沒有
> 什麼時候會需要轉移資料？ 公開轉到不公開，GCS 轉到 AWS
> 會常發生嗎？ 目前不會發生

#### 結論: 依照跟 project 相不相關分類資料

- 以下為 GCS 畫面
![](https://i.imgur.com/n6PDxBP.png)


## 規劃
- 打算寫成一個 module，寫 FiO 上傳檔案的邏輯，未來可以使用此模組來減少重複的 code
- 上傳功能模組功能:
    - 上傳到哪個 storage(GCS, AWS S3, disk...)
    - 定義上傳路徑到哪個 bucket and folder
    - upload / download
    - ~~是否要刪除前一個檔案 e.g. project cover or 大頭貼~~

### Cloud storage 檔案架構

#### Bucket
- 會將 cloud storage 的 bucket 分成 4 個，以環境跟公開不公開作為區分
    - fio-service-dev-public
    - fio-service-dev-private
    - fio-service-prod-public
    - fio-service-prod-private
- 上傳到 disk 就直接將 bucket 名稱設定成資料夾名稱，未來當作 k8s 儲存位置的指向 // 先不實作

#### folder
- 分成 projects / general

### 虛擬碼
```=javascript
    class StorageService {
        construct(storage, folderKey, isPublic){
            this.storage = storage;
            this.bucketName = getBucketName(isPublic)
            // or
            if(storage === 'gcs') {
                this.storage = new GCS();
            } else if (storage == 's3') {
                this.storage = new S3();
            } else {
                throw error
            }
            this.bucketName = getBucketName(isPublic)
        }
    }
    
    getBucketName(isPublic) {
        check isPublic and env
        return BucketName;
    }
    
    upload (filePath, isOnly, id) {
        if (isOnly === true) {
            用 id 改檔名
            filePath = newFilePath
        }
        this.storage.upload(filePath, bucketName, folderName);
    }
    
    download (url) {
        this.storage.download(url);
    }
    
```