--- 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); } ```