상세 컨텐츠

본문 제목

[Django] 파일 업로드 & 다운로드 (feat. Model)

프로그래밍 언어/Python(파이썬)

by 알 수 없는 사용자 2022. 11. 24. 16:46

본문

장고에서 모델 클래스를 사용한 파일 업로드 & 다운로드


파일 업로드

1. 템플릿 파일 생성 및 작성

먼저 임의의 템플릿 파일을 생성한다. 또한 해당 템플릿 파일로 이동할 수 있도록 뷰(view) 파일에 뷰를 작성하고, 뷰가 동작할 수 있도록 URL 구성(URLconf) 파일에 URL 양식(URL pattern)을 등록한다. 장고 기본 문법이므로 자세한 코드는 생략한다.

이후, 데이터를 뷰로 전송하기 위한 form 태그, 사용자로부터 파일을 입력받기 위한 input 태그를 템플릿 파일에 작성한다.

<!DOCTYPE>
<html>
    <head>
        <meta charset="utf-8">
        <title>파일 업로드</title>
    </head>
    <body>
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <input type="file" name="attachedFile">
            <input type="submit" value="업로드">
        </form>
    </body>
</html>

form 태그에 action 속성을 추가하고 임의로 원하는 값을 작성한다. 또한 이를 처리하기 위해 마찬가지로 임의의 뷰를 작성하고 URL 양식을 등록한다. 위 코드 예시에서는 생략되었다.

form 태그에 method="post" 속성을 작성한다. method 속성의 값이 post이기 때문에 form 태그 안에 csrf_token 템플릿 태그를 추가한다.

form 태그에 enctype="multipart/form-data" 속성을 작성한다.

input 태그에 type="file" 속성을 작성한다.

input 태그에 name 속성을 추가하고 임의로 원하는 값을 작성한다. 자세한 내용은 후술.

 

2. settings.py 파일에 저장 경로 및 요청 URL 설정

프로젝트 핵심 설정 파일MEDIA_ROOT 변수와 MEDIA_URL 변수를 작성한다.

# settings.py
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = 'media/'

MEDIA_ROOT: 파일을 저장할/불러올 경로. 사용자가 업로드/다운로드를 시도한 파일을 이 경로에 저장한다/불러온다. 위 코드 예시에서는 프로젝트 폴더 내 media 폴더에 저장하는 것으로 설정했다.

MEDIA_URL: 위 경로의 파일을 조작할 때 사용할 URL. 이 문자열 값은 반드시 slash(/)로 끝나야 한다.

 

3. 위에서 작성한 값들을 urls.py 파일에 추가

위에서 작성한 미디어 파일 관련 설정 값들을 프로젝트 구성 폴더(settings.py, asgi.py, wsgi.py 등의 파일이 있는 프로젝트 이름 폴더)에 위치한 최상위 URL 구성(root URLconf) 파일static 함수를 사용해서 등록한다.

# urls.py

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

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

이때, 설정 값들은 settings 변수를 통해 불러오고, 설정 값들을 등록할 때 사용하는 static 함수 또한 불러와야 한다.

 

4. model.py 파일에 모델 클래스 작성

파일을 저장하는 방법엔 여러가지가 있지만 가장 쉬운 방법 중 하나는 모델 클래스를 사용하는 것이다.

파일을 다루기 위한 모델 클래스를 아래와 같이 작성한다.

# models.py

from django.db import models

class UploadFile(models.Model):
    attached_file = models.FileField(upload_to='files/%Y-%m-%d/', null=True)
    original_filename = models.CharField(max_length=260, null=True)

attached_file 필드는 파일 그 자체를 다루기 위한 필드로써 FileField 객체로 초기화한다. upload_to 키워드 인수를 추가하여 파일을 저장할 경로를 추가로 작성할 수 있다. 위의 경우, '프로젝트 폴더/media/files/2022-12-31/파일명' 같은 형식으로 저장한다.

original_filename 필드는 원본 파일 이름을 다루기 위한 필드로써 CharField 객체로 초기화한다. 만약 저장하고자 하는 파일과 같은 이름의 파일이 같은 경로에 이미 존재한다면, 장고에서 새로 저장할 파일의 이름을 자동으로 변경해주기 때문에 사용자가 업로드한 원본 파일 이름을 별도로 저장해준다. 또한 파일 이름은 윈도우 운영체제에서 기본적으로 260자까지 사용할 수 있기 때문에 max_length 키워드 인수를 260으로 설정한다.

 

모델 클래스 이름, 필드 이름은 위의 코드 예시와 반드시 일치할 필요는 없다. 프로젝트 상황에 맞춰 원하는 대로 작명할 수 있다.

모델 작성 후, 마이그레이션(migration) 작업을 실행하여 프로젝트에 연결된 데이터베이스에 반영한다.

더보기

유의할 점은 업로드한 파일 그 자체를 데이터베이스에 바로 저장하지 않는다는 것이다. 업로드한 파일을 일일이 데이터베이스에 저장하면 매우 많은 용량이 요구되기 때문에, 일반적으로 파일은 별도의 저장소(여기서는 프로젝트 폴더)에 저장하고 데이터베이스엔 업로드한 파일과 관련된 정보만 저장한다(원본 파일 이름, 파일 형식, 업로드 날짜 등).

 

5. views.py 파일에 뷰 작성 및 업로드 파일 처리

마지막으로 뷰를 작성해서 파일 업로드 기능을 마무리한다.

이 뷰는 앞서 템플릿 파일에서 form 태그의 action 속성을 다룰 때 작성할 것을 언급했던 것이다. 사용자가 템플릿에서 submit 버튼을 클릭하면 form 태그를 통해 POST 방식의 요청이 발생하는데, 해당 요청이 이 뷰를 통해 처리되도록 URL 양식을 알맞게 등록해둬야 한다.

# views.py

from .models import UploadFile

def upload_file(request):
    if request.FILES:
        attached_file = request.FILES.get('attachedFile')
        UploadFile.objects.create(
            attached_file=attached_file,
            original_filename=attached_file.name
        )
    return "업로드 성공"
  • if request.FILES:
    • 사용자가 첨부한 파일은 HttpRequest 객체(=request 변수)의 FILES 변수 안에 들어있다. 이 변수를 조건문에 작성함으로써 템플릿으로부터 파일 데이터가 무사히 전달됐는지 여부를 검사할 수 있다.
  • attached_file = request.FILES.get('attachedFile')
    • 전달된 파일이 존재한다면 FILES 변수는 사전(dictionary) 형식의 객체이기 때문에 request.FILES['attachedFile'] 또는 request.FILES.get('attachedFile') 같은 코드로 파일 데이터를 가져올 수 있다. 이때, 사전의 키(key)에 해당하는 'attachedFile' 문자열 값은 템플릿에서 파일을 입력받는 input 태그의 name 속성의 값과 일치해야 한다.
  • UploadFile.objects.create(...)
    • 모델 클래스의 Manager 객체(=objects 속성)를 통해 새로운 데이터를 생성한다. 이때, 파일 필드에 파일 데이터를 설정하면 자동으로 파일이 저장된다.

또는 다음과 같이 클래스 기반 뷰를 작성할 수도 있다.

# views.py

from django.views.generic import CreateView
from .models import UploadFile

class FileUploadView(CreateView):
    model = UploadFile
    fields = ['attached_file', 'original_filename']
    
    def form_valid(self, form):
        if self.request.FILES:
            attached_file = self.request.FILES['attached_file']
            form.instance.attached_file = attached_file
            form.instance.original_filename = attached_file.name
            form.save(commit=True)
        return super().form_valid(form)

이외에도 form_valid 함수를 사용하지 않거나 모델 폼 클래스를 사용하는 등 다양한 방법으로 파일 업로드 기능을 구현할 수 있다.

일련의 구현을 통해, 사용자가 어느 한 컴퓨터에서 업로드를 요청한 파일을, 장고 프로젝트가 서비스되는 서버 컴퓨터 혹은 또다른 저장소에 저장할 수 있다.


파일 다운로드

파일 다운로드는 좀 더 간단하게 구현할 수 있다.

# views.py

from django.http import FileResponse
from .models import UploadFile

def download_file(request, pk):
    upload_file = UploadFile.objects.get(pk=pk)
    attached_file = upload_file.attached_file
    original_filename = upload_file.original_filename
    response = FileResponse(attached_file)
    response['Content-Disposition'] = 'attachment; filename=%s' % original_file_name
    return response

먼저 다운로드할 파일 정보를 가지고 있는 모델 객체를 선택한다. 이후 FileResponse 객체에 파일 데이터를 첨부하여 반환한다. 이때, 응답에 첨부 파일이 있다는 사항과 파일 이름이 무엇인지 작성해야 정상적으로 파일 다운로드가 동작한다.

관련글 더보기

댓글 영역