Dev./Django & DRF

Django: S3 호환 서비스 NCP object storage 에 이미지 저장

Ivan'show 2023. 10. 19.
728x90
반응형

Django 프로젝트에서 이미지 핸들링하기

이미지 파일을 DB 에 저장하기엔 용량이 크기 떄문에 클라우드 Object Storage 에 저장해서 url 로 가져다 쓴다.

NPC 는 관련해서 AWS S3 호환 서비스 Object Storage 를 제공하고 있기 떄문에 사용해보려고 한다.

 

 

 

AWS S3

 

AWS boto3 를 이용한 파일 업로드

boto3 는 AWS 를 위한 Python SDK (Software Development Kit) 이다. 사용하면 AWS 서비스에 쉽게 액세스 할 수있다. NCP 의 Object Storage 와 같은 S3 호환 서비스에도 사용이 가능하다.

Object Storage API

 

Object Storage API

 

api.ncloud-docs.com

 

간단한 사용 방법

HTTP/HTTPS 호출 도메인 #Korea

<https://kr.object.ncloudstorage.com>

boto 3 설치

pip install boto3

AWS 서비스에 액세스

여기에선 NCP 로 접속해야하니 NCP 의 Access key 와 Secret key 사용

import boto3

s3 = boto3.client('s3', aws_access_key_id='YOUR_ACCESS_KEY', aws_secret_access_key='YOUR_SECRET_KEY')

버킷을 조회하거나 생성

# 버킷 리스트 조회
response = s3.list_buckets()
buckets = [bucket['Name'] for bucket in response['Buckets']]

# 버킷 생성
s3.create_bucket(Bucket='my_bucket')

업로드, 다운로드, 삭제

# 파일 업로드
s3.upload_file('myfile.txt', 'my_bucket', 'myfile.txt')

# 파일 다운로드
s3.download_file('my_bucket', 'myfile.txt', 'myfile_downloaded.txt')

# 파일 삭제
s3.delete_object(Bucket='my_bucket', Key='myfile.txt')

S3 호환 스토리지 서비스 (e.g. NCP) 에서 사용할 경우 endpoint_url 사용

s3 = boto3.client('s3', endpoint_url='https://your_endpoint_url', aws_access_key_id='YOUR_ACCESS_KEY', aws_secret_access_key='YOUR_SECRET_KEY')

 

NCP 로 Django 서비스에 적용해보기

실제 사용 코드

# image_handler - store, menu 등에서 이미지를 핸들링 하기 위해 utils 에 생성

def image_handler(request, exsiting_post=None):
    image_url = exsiting_post if exsiting_post else None

    try:
        if request.FILES.get("store_pic"):
            folder_name = "sajjang_store"
            image = request.FILES.get("store_pic")
        elif request.FILES.get("menu_pic"):
            print(request.FILES.get("menu_pic"))
            folder_name = "sajjang_menu"
            image = request.FILES.get("menu_pic")
        else:
            print("FILES.get ?, neither store_pic nor menu_pic")
    except Exception as e:
        print(e)
        return JsonResponse({"error": str(e)}, status=400)

    if image:
        image: File
        endpoint_url = settings.IMAGE_BUCKET_ENDPOINT
        access_key = settings.NCP_ACCESS_KEY
        secret_key = settings.NCP_SECRET_KEY
        buket_name = "del-app-mh"

        s3 = boto3.client(
            "s3",
            endpoint_url=endpoint_url,
            aws_access_key_id=access_key,
            aws_secret_access_key=secret_key,
        )
        image_id = str(uuid.uuid4())

        # split 이후 마지막 값 가져오기 (jpg, png, gif 등등)
        file_extension = image.name.split(".")[-1]
        image_name = f"{image_id}.{file_extension}"
        s3_key = f"{folder_name}/{image_name}"

        try:
            s3.upload_fileobj(image.file, buket_name, s3_key)
            s3.put_object_acl(ACL="public-read", Bucket=buket_name, Key=s3_key)
        except Exception as e:
            print(e)
            return JsonResponse({"error": str(e)}, status=400)

        image_url = f"{endpoint_url}/{buket_name}/{s3_key}"
        print("image_url out:", image_url)

    return image_url
# post 메서드에서 image_handler 를 불러와 적용
def post(self, request):
        image_url = image_handler(request)

        try:
            name = request.POST["store_name"]
            address = request.POST["store_address"]
            store_pic = image_url if request.FILES.get("store_pic") else None
            status = request.POST.get("status", False)

            user = get_object_or_404(User, id=request.user.pk)
            category = get_object_or_404(Category, id=request.POST["category"])

            new_store = Stores(
                user_id=user,
                name=name,
                address=address,
                store_pic=store_pic,
                category_id=category,
                status=status,
            )
            new_store.save()

            return redirect("sajjang:sajjang_home")
        except Exception as e:
            return JsonResponse({"error": str(e)}, status=400)
{% comment %} url 로 저장되어 있기 때문에 바로 적용 {% endcomment %}
<div class="my-2" style="width:100px; height:100px;">
  {% if store.store_pic %}
      <img src="{{ store.store_pic }}" class="rounded float-start bg-secondary" alt="" style="width:100px; height:100px;">
  {% else %}
      <img src="..." class="rounded float-start bg-secondary" alt="No img" style="width:100px; height:100px;">
  {% endif %}
</div>

 

728x90
반응형

댓글