수학과의 좌충우돌 프로그래밍

[Django] media 파일 업로드하기 본문

웹프로그래밍/Django

[Django] media 파일 업로드하기

ssung.k 2019. 7. 30. 01:02

Media 파일이란?

media 파일이란 FileField 를 통해 저장한 모든 파일을 지칭합니다. 물론 ImageField 도 FileField 를 상속받은 필드로서 유사 필드들을 통해 저장된 파일도 media 파일입니다. 이는 특별하게 db 필드에는 저장경로를 저장하고 파일은 뒤에서 다시 다루겠지만 settings.MEDIA_ROOT 경로에 저장 하게 됩니다.

settings 에서의 설정

  • MEDIA_URL

    MEDIA_URL = '/media/'
    

    각 media 파일에 대한 URL 의 고정값을 설정할 수 있습니다. 예시를 들자면 필드명.url 에 의해 MEDIA 파일에 대하여 접근할 때 결정되는 값입니다.

  • MEDIA_ROOT

    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    

    FileField 를 통해서 저장 시 db 에는 파일의 경로가 문자열로 저장된다고 앞에서도 언급하였습니다. 그래서 실제 파일이 저장될 ROOT 경로를 설정합니다.

파일 업로드

위에 설정을 마쳤으니 파일 업로드 하는 과정을 알아보도록 합시다.

# models.py

class Profile(models.Model):
    name = models.CharField(max_length=10)
    photo = models.ImageField(upload_to="")

models 에 ImageField 을 추가해주고 이미지를 받을 수 있도록 했습니다. MEDIA_ROOT 를 지정해줌으로서 해당 경로에 이미지 사진을 저장하게 됩니다. 위의 예시 같은 경우에는 BASE_DIR/media 위치에 저장이 될 것이고 상황에 따라 스토리지 등에 저장을 할 수 있습니다. 그러면 한 폴더 내에 모든 파일이 전부 저장이 되는 것일까요? 모델 옵션을 통해서 이를 구체화 해줄 수 있습니다. 성능적인 부분으로 볼 때도, 한 디렉토리 내에 너무 많은 파일들이 저장되어 있다면 좋지 않습니다. 따라서 위 처럼 upload_to 옵션으로서 이를 지정해줄 수 있습니다.

makemigrations 시 오류가 뜬다면?
file 을 다루기 위해서는 파이썬 패키지인 pillow 가 필요합니다. 따라서 pillow 를 install 해주도록 합니다. 

  • upload_to

    두 가지 인자로 경로를 지정할 수 있습니다.

    • 문자열

      문자열로 지정할 경우, 중간 디렉토리 경로를 지정

      # media/image/ 아래에 저장
      photo = models.ImageField(upload_to="image")
      # 이미지 업로드 날짜에 따라 디렉토리에 저장 (strftime 으로 포멧팅)
      photo = models.ImageField(upload_to="%Y/%m/%d")
      
    • 함수

      함수로 지정할 경우, 중간 디렉토리 및 파일명까지 결정 가능

      import os
      from uuid import uuid4
      from django.utils import timezone
      
      def date_upload_to(instance, filename):
        # upload_to="%Y/%m/%d" 처럼 날짜로 세분화
        ymd_path = timezone.now().strftime('%Y/%m/%d') 
        # 길이 32 인 uuid 값
        uuid_name = uuid4().hex
        # 확장자 추출
        extension = os.path.splitext(filename)[-1].lower()
        # 결합 후 return
        return '/'.join([
          ymd_path,
          uuid_name + extension,
        ])
      
      photo = models.ImageField(upload_to=date_upload_to)
      
<!-- index.html -->

<form action="" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
  <!-- 생략 -->
</form>

form 으로 부터 이미지를 입력을 받을 시에는 두 가지를 설정해주셔야 합니다. (csrf_token은 당연한 과정이므로 굳이 설명하지 않겠습니다.) method 는 POST로, enctype 도 다음과 같이 설정을 해주셔야 합니다. enctype 을 설정하지 않으신다면 파일은 넘어오지 않고 파일 이름만 넘어오게 됩니다.

# views.py

def create(request):
  # 생략
  profile.photo = request.FILES['photo']

파일을 받을 떄는 다음과 같이 request.FILES 로 받게 됩니다.

# config/urls.py

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

static 파일과 다르게 장고는 개발 서버에서 서빙을 지원해주지 않습니다. 쉽게 설명하면 지금 이런 과정을 해봤자 말짱 도루묵이라는 거죠. 다행히도 직접 서빙 rule를 추가할 수 있습니다. MEDIA_URL 에 대한 요청이 오면 MEDIA_ROOT 에서 찾게 됩니다. 다음 설정은 DEBUG 가 True 일 때만 동작하고 false 일 때는 빈 리스트를 return 합니다.

저장한 MEDIA 파일 사용

저장된 media 파일을 사용하기 위해서는 두 가지 속성을 알아야합니다. urlpath 입니다.

<!-- 1번 -->
<img src="{{ profile.photo }}" >
<!-- 2번 -->
<img src="{{ profile.photo.url }}" >
<!-- 3번 -->
<img src="{{ profile.photo.path }}" >

3가지의 차이를 알아봅시다.

1번은 우리가 원하는 결과를 도출할 수 없습니다. 계속 말하지만 db에는 파일의 경로가 문자열로 저장되어 있기 때문에 저 부분은 그저 문자열을 참조할 뿐이죠.

2,3 번은 우리가 원하는 결과를 얻을 수 있습니다. 둘의 차이는 url 은 media 폴더 부터 시작하는 상대 경로를, path 는 절대경로를 나타냅니다. 뭐가 되었든 파일이 저장되어있는 media 파일을 참조하기 때문에 이미지를 불러올 수 있습니다.

해당 photo 가 존재하지 않는다면 이는 경로를 찾지 못해서 오류가 나기 때문에 다음과 같이 오류를 예방해주는 것도 좋은 방법입니다.

{% if profile.photo %}
    <img src="{{ profile.photo.url }}" >
{% endif %}
Comments