Class S3FileStorage

java.lang.Object
io.leandev.appfuse.file.s3.S3FileStorage
All Implemented Interfaces:
FileStorage

public class S3FileStorage extends Object implements FileStorage

S3/MinIO 檔案儲存實作

將檔案儲存在 S3 或相容服務(如 MinIO)。適用於雲端部署或需要分散式儲存的場景。

物件 Key 結構

{bucket}/
├── {keyPrefix}/staging/               # 暫存區
│   └── {tenantId}/
│       └── {date}/{uuid}              # 暫存檔案
└── {keyPrefix}/files/                 # 永久區
    └── {tenantId}/
        └── {yyyy-MM-dd}/{HH}/
            └── {filename-shortUuid}.{ext}

使用方式

FileStorage fileStorage = S3FileStorage.builder()
    .s3Client(s3Client)
    .s3Presigner(s3Presigner)  // 用於 Presigned URL
    .bucket("my-bucket")
    .keyPrefix("appfuse")
    .presignedUploadEnabled(true)
    .presignedDownloadEnabled(true)
    .presignedUrlExpiration(Duration.ofHours(1))
    .maxFileSize(50 * 1024 * 1024)  // 50MB,選填
    .cdnUrl("https://cdn.example.com")  // 選填,CDN URL
    .idGenerator(new FileIdGenerator())
    .build();

檔案大小限制

可選的 maxFileSize 參數用於限制檔案大小。設定後會在 prepareStaging 和 store 時檢查。 若未設定(值為 0 或負數),則不進行大小限制。

getUrl() 行為

getUrl(String, String) 方法的回傳值依設定而定:

  • Method Details

    • builder

      public static S3FileStorage.Builder builder()
    • prepareStaging

      public FileStorage.StagingUploadInfo prepareStaging(String tenantId, String filename, String contentType, long size)
      Description copied from interface: FileStorage

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

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

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

      public void completeStagingUpload(String tenantId, String tempId, InputStream content, long size)
      Description copied from interface: FileStorage

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

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

      Specified by:
      completeStagingUpload in interface FileStorage
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID(由 prepareStaging 回傳)
      content - 檔案內容的 InputStream
      size - 檔案大小
    • getStagedMetadata

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

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

      public boolean existsStaged(String tenantId, String tempId)
      Description copied from interface: FileStorage
      檢查暫存檔案是否存在
      Specified by:
      existsStaged in interface FileStorage
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
      Returns:
      是否存在
    • deleteStaged

      public void deleteStaged(String tenantId, String tempId)
      Description copied from interface: FileStorage
      刪除暫存檔案
      Specified by:
      deleteStaged in interface FileStorage
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
    • persist

      public String persist(String tenantId, String tempId)
      Description copied from interface: FileStorage

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

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

      Specified by:
      persist in interface FileStorage
      Parameters:
      tenantId - 租戶 ID
      tempId - 暫存檔案 ID
      Returns:
      fileId(唯一識別碼,同時也是可讀的路徑格式)
    • store

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

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

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

      public boolean exists(String tenantId, String fileId)
      Description copied from interface: FileStorage
      檢查永久區檔案是否存在
      Specified by:
      exists in interface FileStorage
      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
      Returns:
      是否存在
    • delete

      public void delete(String tenantId, String fileId)
      Description copied from interface: FileStorage
      刪除永久區檔案
      Specified by:
      delete in interface FileStorage
      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
    • getUrl

      public String getUrl(String tenantId, String fileId)

      取得檔案的存取 URL

      優先順序:Presigned URL > CDN URL > 拋出例外

      Specified by:
      getUrl in interface FileStorage
      Parameters:
      tenantId - 租戶 ID
      fileId - 檔案 ID
      Returns:
      存取 URL(僅雲端儲存支援)
      Throws:
      UnsupportedOperationException - 若未啟用 Presigned URL 且未設定 CDN URL
    • getStorageType

      public String getStorageType()
      Description copied from interface: FileStorage
      取得儲存類型名稱
      Specified by:
      getStorageType in interface FileStorage
      Returns:
      儲存類型(例如 "local", "azure", "s3", "database", "sftp")
    • isPresignedUploadEnabled

      public boolean isPresignedUploadEnabled()
      檢查是否啟用 Presigned 上傳