如何避免重複存儲不同格式圖片在 AWS S3:新手指南

更新日期: 2024 年 12 月 21 日

在處理圖片上傳時,我們經常需要將圖片轉換為更高效的格式(如 WebP)以提升性能。

然而,如果處理後的圖片(WebP)與原始圖片(PNG、JPG)都存儲在 AWS S3 中,可能會導致存儲空間浪費和管理負擔。

本指南旨在幫助新手學習如何在圖片轉換完成後,刪除原始圖片,確保只有處理後的 WebP 文件被保留。


問題分析:圖片重複存儲的原因

當我們使用 Django 上傳圖片並進行格式轉換時,常見的邏輯流程如下:

  1. 用戶上傳圖片(例如 example.jpg)。
  2. 圖片被保存到 AWS S3。
  3. 圖片轉換為 WebP 格式後,處理後的文件(如 example.webp)也被上傳到 S3。
  4. 但此時,原始文件(example.jpg)並未被刪除。

這會導致同一張圖片存在兩個版本(原始和處理後),既浪費了存儲空間,也讓管理變得複雜。


解決方法:上傳 WebP 文件後刪除原始圖片

我們可以在處理完成後,使用 AWS S3 的 delete_object 方法刪除原始圖片。

修改 save() 方法

下面是一個完整的代碼示例,展示如何在轉換 WebP 文件後刪除原始文件。

from io import BytesIO
from PIL import Image
import boto3
import uuid
from django.conf import settings
from django.db import models

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    photo = models.ImageField(upload_to="profile_photos/", blank=True, null=True)

    def save(self, *args, **kwargs):
        # 保存模型其他字段
        super().save(*args, **kwargs)

        if self.photo:
            try:
                # 打開原始圖片
                image = Image.open(self.photo)

                # 將圖片轉換為 WebP
                webp_image_io = BytesIO()
                image.save(webp_image_io, format="WEBP", quality=85)
                webp_image_io.seek(0)

                # 生成 WebP 文件唯一名稱
                unique_filename = f"profile_photos/{uuid.uuid4()}.webp"

                # 初始化 S3 客戶端
                s3 = boto3.client("s3", region_name=settings.AWS_S3_REGION_NAME)

                # 上傳 WebP 文件到 S3
                s3.upload_fileobj(
                    webp_image_io,
                    settings.AWS_STORAGE_BUCKET_NAME,
                    unique_filename,
                    ExtraArgs={"ContentType": "image/webp"},
                )

                # 獲取原始檔案的路徑
                original_file_path = self.photo.name

                # 更新 photo 字段為新的 WebP 文件路徑
                self.photo = unique_filename

                # 保存更新後的 photo 字段
                super().save(*args, **kwargs)

                # 刪除原始檔案
                s3.delete_object(
                    Bucket=settings.AWS_STORAGE_BUCKET_NAME,
                    Key=original_file_path,
                )
            except Exception as e:
                raise RuntimeError(f"Error converting image to WebP: {e}")

詳細解釋代碼邏輯

步驟 1:保存模型其他字段

super().save(*args, **kwargs)
  • 先保存模型的其他字段(例如名稱、描述等),確保基本信息被保存。

步驟 2:處理圖片

  • 使用 Pillow 打開原始圖片,並將其轉換為 WebP 格式:
image = Image.open(self.photo)
webp_image_io = BytesIO()
image.save(webp_image_io, format="WEBP", quality=85)
webp_image_io.seek(0)

步驟 3:生成唯一文件名

  • 使用 UUID 生成不重複的文件名:
unique_filename = f"profile_photos/{uuid.uuid4()}.webp"

步驟 4:上傳 WebP 文件

  • 使用 AWS S3 的 upload_fileobj 方法上傳處理後的圖片:
s3.upload_fileobj(
    webp_image_io,
    settings.AWS_STORAGE_BUCKET_NAME,
    unique_filename,
    ExtraArgs={"ContentType": "image/webp"},
)

步驟 5:刪除原始文件

  • 使用 delete_object 方法刪除原始文件:
original_file_path = self.photo.name
s3.delete_object(
    Bucket=settings.AWS_STORAGE_BUCKET_NAME,
    Key=original_file_path,
)

測試場景

情境 1:上傳新圖片

  1. 用戶上傳圖片(如 example.jpg)。
  2. 程式保存該圖片,轉換為 WebP 格式(如 example.webp),並刪除原始文件。

情境 2:更新圖片

  1. 用戶上傳新的圖片(如 new_example.jpg)。
  2. 程式保存新圖片,生成新的 WebP 文件,刪除新的原始文件。

情境 3:未更改圖片

  1. 如果用戶未更改圖片,self.photo 未更新,圖片處理邏輯不會被執行,也不會刪除任何文件。

注意事項

權限設置

  • 確保 AWS S3 的 IAM 策略允許刪除操作: { "Effect": "Allow", "Action": "s3:DeleteObject", "Resource": "arn:aws:s3:::your-bucket-name/*" }

錯誤處理

  • 如果刪除原始文件失敗,建議記錄錯誤日誌以便後續排查。

節省成本

  • 通過刪除原始文件,可以節省存儲成本,特別是在大規模圖片處理場景中。

總結

通過在圖片轉換後刪除原始文件,我們可以有效地避免同一張圖片的多個版本被存儲,從而節省存儲空間並簡化管理。

本文介紹的方法適合各類需要進行圖片處理和雲存儲的應用場景,希望對新手開發者有所幫助。

Similar Posts