Interface FileStorage

All Known Implementing Classes:
AzureBlobFileStorage, LocalFileStorage, S3FileStorage, SftpFileStorage

public interface FileStorage

統一檔案儲存介面

整合暫存區與永久區操作,支援多種儲存後端(Local、Azure Blob、S3、Database、SFTP)。

設計原則

  • 框架層純 Java 介面,不依賴 Spring
  • 業務層只需處理 fileId,不需關心儲存細節
  • fileId 格式:yyyy-MM-dd/HH/filename-shortUuid.ext
  • 串流優先:使用 InputStream 避免大檔案載入記憶體
  • 呼叫端負責關閉 InputStream

使用流程

// 1. 準備暫存上傳
StagingUploadInfo info = fileStorage.prepareStaging(tenantId, filename, contentType, size);

// 2. 完成暫存上傳
fileStorage.completeStagingUpload(tenantId, info.tempId(), inputStream, size);

// 3. 持久化到永久區
String fileId = fileStorage.persist(tenantId, info.tempId());

// 4. 讀取永久區檔案
FileMetadata meta = fileStorage.getMetadata(tenantId, fileId).orElseThrow();
try (InputStream is = fileStorage.getInputStream(tenantId, fileId)) {
    // 處理檔案內容
}

支援的儲存類型

  • local - 本地檔案系統
  • database - 資料庫 BLOB(有檔案大小限制)
  • s3 - AWS S3 或相容服務(如 MinIO)
  • azure - Azure Blob Storage(支援 Azurite 模擬器)
  • sftp - SFTP 檔案伺服器(支援密碼/私鑰驗證)
  • Method Details

    • prepareStaging

      FileStorage.StagingUploadInfo prepareStaging(String tenantId, String filename, String contentType, long size)

      準備暫存上傳 - 統一上傳流程的第一步

      前端先呼叫此方法取得上傳 URL,再透過 PUT 上傳檔案內容。 此設計讓所有儲存類型(Local、Database、S3)使用統一的前端上傳邏輯。

      Parameters:
      tenantId - 租戶 ID
      filename - 原始檔名
      contentType - MIME 類型
      size - 檔案大小
      Returns:
      StagingUploadInfo(含 tempId 和上傳 URL)
    • completeStagingUpload

      void completeStagingUpload(String tenantId, String tempId, InputStream content, long size)

      完成暫存上傳 - 統一上傳流程的第二步

      接收 PUT 請求的檔案內容,完成暫存區上傳。 此方法由 Controller 在收到 PUT 請求時呼叫。

      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID(由 prepareStaging 回傳)
      content - 檔案內容的 InputStream
      size - 檔案大小
      Throws:
      IllegalArgumentException - 若 tempId 不存在或已過期
    • getStagedMetadata

      Optional<FileStorage.FileMetadata> getStagedMetadata(String tenantId, String tempId)
      取得暫存檔案的 Metadata
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
      Returns:
      檔案 Metadata,若不存在則回傳 empty
    • getStagedInputStream

      InputStream getStagedInputStream(String tenantId, String tempId) throws FileNotFoundException
      取得暫存檔案的 InputStream
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
      Returns:
      InputStream(呼叫端負責關閉)
      Throws:
      FileNotFoundException - 若檔案不存在
    • existsStaged

      boolean existsStaged(String tenantId, String tempId)
      檢查暫存檔案是否存在
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
      Returns:
      是否存在
    • deleteStaged

      void deleteStaged(String tenantId, String tempId)
      刪除暫存檔案
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
    • persist

      String persist(String tenantId, String tempId)

      將暫存檔案持久化到永久區

      自動生成唯一的 fileId,格式為 yyyy-MM-dd/HH/filename-shortUuid.ext。 業務層只需儲存回傳的 fileId,不需關心檔案實際儲存位置。

      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
      Returns:
      fileId(唯一識別碼,同時也是可讀的路徑格式)
      Throws:
      IllegalArgumentException - 若暫存檔案不存在
    • store

      String store(String tenantId, String filename, InputStream content, String contentType, long size)
      直接儲存檔案到永久區(跳過暫存)
      Parameters:
      tenantId - 租戶 ID
      filename - 原始檔名(用於生成 fileId)
      content - 檔案內容的 InputStream
      contentType - MIME 類型
      size - 檔案大小
      Returns:
      fileId
    • getMetadata

      Optional<FileStorage.FileMetadata> getMetadata(String tenantId, String fileId)
      取得永久區檔案的 Metadata
      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
      Returns:
      檔案 Metadata,若不存在則回傳 empty
    • getInputStream

      InputStream getInputStream(String tenantId, String fileId) throws FileNotFoundException
      取得永久區檔案的 InputStream
      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
      Returns:
      InputStream(呼叫端負責關閉)
      Throws:
      FileNotFoundException - 若檔案不存在
    • exists

      boolean exists(String tenantId, String fileId)
      檢查永久區檔案是否存在
      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
      Returns:
      是否存在
    • delete

      void delete(String tenantId, String fileId)
      刪除永久區檔案
      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
    • getUrl

      String getUrl(String tenantId, String fileId)

      取得檔案的存取 URL

      注意:此方法僅適用於雲端儲存(S3、Azure Blob),可回傳 Presigned URL 或 CDN URL。 對於 Local 和 Database 儲存類型,此方法會拋出 UnsupportedOperationException, 因為檔案下載應由業務層 Controller 實作以確保權限控管。

      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
      Returns:
      存取 URL(僅雲端儲存支援)
      Throws:
      UnsupportedOperationException - 若儲存類型不支援 URL 存取(如 Local、Database)
    • getStorageType

      String getStorageType()
      取得儲存類型名稱
      Returns:
      儲存類型(例如 "local", "azure", "s3", "database", "sftp")