Class ImageResponseBuilder

java.lang.Object
io.leandev.appfuse.file.ImageResponseBuilder

public class ImageResponseBuilder extends Object

圖片下載 Response 建構器(支援伺服端縮圖 + 條件式下載)

專責「經轉換(縮圖 / 格式轉換)」的圖片回應,與 FileResponseBuilder(原樣串流 + Range)分工。核心價值是把 ETag 做對且省

  • 變體 ETag:ETag = "{fileId}-w{width}-h{height}-{format}-t{transformVersion}", 把尺寸與格式納入驗證器 → 不同尺寸各自快取、不會互相拿到錯圖(by construction)。
  • pre-decode 304:在解碼之前就以 ETag 比對 If-None-Match,命中直接回 304, 完全跳過昂貴的 decode → scale → encode。
  • buffered 回應:縮圖輸出大小需編碼後才知,故走記憶體 buffered(正確 Content-Length)。 本 builder 不自行解析 Range header(不像 FileResponseBuilder 對串流做 byte-skip); 但因回應體為記憶體 ByteArrayResource,Spring 的 ResourceHttpMessageConverter 會自動就已編碼的緩衝內容提供 range(回 206)——這是連貫的, 因為整張縮圖在回應時已完整編碼於記憶體。大檔仍建議用 FileResponseBuilder:它串流 原始 bytes(低記憶體),而 resize 會把整個輸出載入記憶體。

無縮放需求(未呼叫 resize(Integer, Integer) 或寬高皆 null)時,自動委派 FileResponseBuilder 服務原圖 (原圖 ETag = fileId、支援 Range)。

使用範例

@GetMapping("/{id}/image")
public ResponseEntity<Resource> getImage(
        @PathVariable String id,
        @RequestParam(required = false) Integer width,
        @RequestParam(required = false) Integer height,
        @RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) {

    Product product = productService.findById(id);  // 權限檢查
    String partition = TenantContext.getCurrentTenantId();

    return ImageResponseBuilder.from(fileStorage, partition, product.getImageFileId())
            .resize(width, height)
            .ifNoneMatch(ifNoneMatch)
            .build();
}
See Also:
  • Method Details

    • from

      public static ImageResponseBuilder from(FileStorage fileStorage, String partition, String fileId)
      建立 ImageResponseBuilder
      Parameters:
      fileStorage - FileStorage 實例
      partition - 儲存分區鍵
      fileId - 檔案 ID
      Returns:
      ImageResponseBuilder 實例
    • resize

      public ImageResponseBuilder resize(Integer width, Integer height)

      設定縮圖目標尺寸(等比縮小,只縮不放)

      寬高皆可為 null(表示該維度不限制);皆為 null 時視為不縮放、退回原圖服務。 圖片一律保持長寬比、且僅在超過目標範圍時才縮小(見 ImageScaler.shrink(BufferedImage, Integer, Integer))。

      Parameters:
      width - 最大寬度(null 表不限制)
      height - 最大高度(null 表不限制)
      Returns:
      this
    • format

      public ImageResponseBuilder format(String format)

      設定輸出格式(如 "png"、"jpeg")

      未設定時依原檔 content-type 的 subtype 推導(image/jpegjpeg), 即預設不做跨格式轉換、保留原格式。

      Parameters:
      format - ImageIO 支援的格式字串
      Returns:
      this
    • filename

      public ImageResponseBuilder filename(String filename)
      設定下載時的檔名(未設定使用原始檔名)
      Parameters:
      filename - 顯示檔名
      Returns:
      this
    • forceDownload

      public ImageResponseBuilder forceDownload()
      強制下載(Content-Disposition: attachment)而非 inline 顯示
      Returns:
      this
    • ifNoneMatch

      public ImageResponseBuilder ifNoneMatch(String ifNoneMatch)

      設定請求的 If-None-Match header(啟用條件式下載)

      命中時在解碼前直接回 304,跳過 decode/scale/encode。

      Parameters:
      ifNoneMatch - 請求的 If-None-Match header 值,可為 null
      Returns:
      this
    • immutable

      public ImageResponseBuilder immutable()

      宣告內容不可變,套用長效快取(見 FileResponseBuilder.immutable()

      縮圖是原檔(write-once)的決定性衍生,同一 (fileId, 尺寸, 格式) 的輸出永不改變, 故變體亦可安全長效快取。

      Returns:
      this
    • cacheControl

      public ImageResponseBuilder cacheControl(org.springframework.http.CacheControl cacheControl)
      覆寫 Cache-Control 策略(預設 CacheControl.noCache
      Parameters:
      cacheControl - Cache-Control 策略
      Returns:
      this
    • build

      public org.springframework.http.ResponseEntity<org.springframework.core.io.Resource> build()

      建構 ResponseEntity

      • 檔案不存在 → 404 Not Found
      • 無縮放需求(寬高皆 null)→ 委派 FileResponseBuilder 服務原圖
      • If-None-Match 與變體 ETag 相符 → 304(解碼前短路)
      • 否則 → 200 OK(縮圖後的 buffered 回應)
      • 來源非可解碼影像 → 退回原圖服務
      Returns:
      ResponseEntity