본문 바로가기
개발/Django_python 웹 프레임워크

Django-Media파일, 파일 업로드

by 자유로운 코끼리 2020. 7. 15.
728x90

"Media" 파일 = 웹 서비스 이용자들이 업로드하는 파일 

 

Static vs Media

Static

Media

이미 가지고 있는 파일들을 보여주는 것이기에 
외부와 통신할 필요 없음.
사용자가 업로드한 파일을 받아와야 하는 것이기에
외부와의 통신 필요
settings.py에서 static 파일이 어디에 있고,
어디로 모을지를 설정해줌.
settings.py에서 media 파일이 어느 url을 타고,
어디로 모을지를 설정해줌.

 

Media

media 파일이란 FileField 를 통해 저장한 모든 파일을 지칭합니다.

ImageField 도 FileField 를 상속받은 필드로서 해당 필드로 저장된 파일도 media 파일입니다.

이는 db 필드에는 저장경로를 저장하고 파일은 settings.MEDIA_ROOT 경로에 저장 하게 됩니다.

 

또 media는 외부에서 값이 들어오는 형태잖아요?

쟝고는 url을 통해 외부와 통신하기 때문에,

외부에서 값을 받아오는 media를 사용하기 위해 url 설정이 필요합니다.

1) 사용자가 이미지를 업로드 하면 이미지가 저장될 장소 설정 ->setting.py

  • MEDIA_URL
MEDIA_URL = '/media/'

각 media 파일에 대한 URL 의 고정값을 설정할 수 있습니다.

http://127.0.0.1:8000/media/파일경로 형태가 되게 하기 위해서 저렇게 설정해준 것입니다.

 

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

미디어 파일이 저장될 경로를 설정한 부분입니다.

 

  • “STATICFILES_DIRS는 있는데 왜 MEDIAFILES_DIRS 설정은 없죠?”

Static의 경우 개발하며 직접적으로 관리해주어야 하는 파일이지만,

Media 파일은 웹사이트 이용자가 올리는 파일이기 때문에

App 차원에서 직접 관리할 필요가 없습니다. 

 

 

2) media에 파일 올리기

 

(1)  #models.py

class Profile(models.Model):
    title = models.CharField(max_lenght=255)
    image = models.ImageField(upload_to='images/')
    
    def __str__(self):
        return self.title

models 에 ImageField를 통해 이미지를 받을 수 있도록 했습니다.

여기에 사용자가 업로드 한 이미지가 들어가게 되겠죠?

이때 MEDIA_ROOT 를 지정해줌으로서, 해당 경로에 이미지 사진을 저장하게 됩니다.

 

추가로 upload_to 옵션을 넣은 것은 BASE_DIR/media/images/아래에 저장해달라는 말입니다.

이렇게 해주는 이유는 각 이미지를 구분하여 저장하기 위함입니다.

만약 이 옵션을 사용하지 않으면 MEDIA_ROOT에서 지정해준 대로 BASE_DIR/media에 저장이 되겠죠?

 

(2)  #터미널

이미지를 데이터베이스에 넣고 싶을 때는 다음과 같은 패키지가 필요합니다.

pip install pillow

PIL(Python Image Library)의 일종, 파이썬으로 이미지들을 처리하고 싶을 때 사용합니다.

 

그리고 그 다음 db에 알려줘야겠죠-? 

python manage.py makemigrations
python manage.py migrate

(3)  #admins.py

from .models import Profile

admin.site.register(Profile)

 

(4)  #upload.html 

<form action="{% url 'upload_create' %}" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    제목 <br>
    <input type="text" name="title"> <br>
    내용 <br>
    <input type="file" name="image"><br>
    <input type="submit" value="Submit" />
</form>

method 는 POST로, enctype 도 다음과 같이 설정을 해주셔야 합니다.

enctype 속성은 해당 데이터가 인코딩되는 방법을 명시합니다.

method 속성값이 “post”인 경우에만 사용할 수 있습니다.

또한 속성값이 "multipart/form-data"라는 의미는 모든 문자를 인코딩하지 않는다는 것을 명시합니다.

주로 form 요소가 파일이나 이미지를 서버로 전송할 때 주로 사용하며,

이를 설정하지 않으신다면 파일은 넘어오지 않고 파일 이름만 넘어오게 됩니다.

 

(5) upload.html에 view,url연결-

#views.py

def upload(request):
    return render(request,'upload.html')

def upload_create(request):
    form=Profile()
    form.title=request.POST['title']
    try:
        form.image=request.FILES['image']
    except: #이미지가 없어도 그냥 지나가도록-!
        pass
    form.save()
    return redirect('/myprofile/profile/')   

여기서 특이점은 파일 및 이미지를 받을 때는 request.FILES을 이용해야 한다는 점입니다.

이때 파일의 저장경로가 문자열로 db에 들어가고 실제 저장은 해당경로에 되게 됩니다.

 

try-except 는 예외처리를 하기 위해 넣어준 건데요.

try:
    실행할 코드
except:
    예외가 발생했을 때 처리하는 코드

 이미지를 필수적으로 넣지 않아도, 처리할 수 있게끔 하기 위해 넣어봤습니다.

 

#urls.py

    path('myprofile/upload/',myprofile.views.upload,name="upload"),
    path('myprofile/upload_create/',myprofile.views.upload_create,name="upload_create"),

3) url 

여태 url에 넣었던 것들은 웹페이지의 경로들이었습니다.

하지만 때때로 페이지뿐만 아니라 파일들도 경로가 필요합니다.

 

static 또한 “STATIC_URL로 설정한 폴더에 있는 이미지 파일을 가져와줘”를 통해 파일을 불러온 것입니다.

이때 static파일의 경우 서버가 어떤 경로에 어떤 파일이 있는지 알지만,

media파일의 경우 서버는 그 파일이 있는지 없는지, 그 이름은 무엇인지를 알 수 없습니다.

 

즉, static 파일은 쟝고에서 직접 serving 해줬지만,

media 파일은 serving하기 위한 url을 짜주어야 합니다.

개발의 편의상 서빙 Rule을 직접 짜주었습니다.

 

이때 static메소드를 이용해 media파일의 url과 경로를 관리해줍니다. 

 

#urls.py

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

MEDIA_URL과 MEDIA_ROOT에 저장되어 있다는 것을 알려줘야 하니,

settings를 import 해줬습니다. 그리고 static 함수를 import 해주었네요.

(이러한 import 관련 경로들은 굳이 외우시지 않아도 됩니다.

필요할 때마다 구글링하시면 됩니다.:)

 

그리곤 다음 코드를 추가해줬는데요.

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

static 함수는  정적 파일들의 url을 관리하는 함수입니다.

 

첫 번째 인자로 Media파일의 URL을,

키워드 인자 document_root로 Media파일이 위치한 경로를 전달했습니다.

 

이는 “Media파일이 있으면 URL에 MEDIA_URL을 통해(-/media),

실제 파일은 MEDIA_ROOT에서 가지고와줘.”라는 의미라고 보시면 됩니다.

 

이때 static 함수를 urlpateerns라는 리스트에 바로 넣지 않고

+=를 한 것은 static 함수가 여러개의 url을 return 할 수도 있기 때문입니다.

 

 

4) 결과화면 보이는 profile.html

 

#views.py

def profile(request):
    profile=Profile.objects.all()
    return render(request,'profile.html',{'profile':profile})

#profile.html

{% for file in profile %}
    <h1> {{file.title}} </h1> 
    <img src="{{ file.image.url }}">
    <br><br>
{% endfor %}

 

file.image에서 .url을 추가한 이유에 대해서 알고 싶다면 url과  path라는 두 가지 속성에 대해 아셔야 합니다.

<!-- 1번 -->
<img src="{{ file.image }}" >
<!-- 2번 -->
<img src="{{ file.image.url }}" >
#<!-- 3번 -->
#<img src="{{ file.image.path }}" >

1번의 경우, db에는 파일의 경로가 문자열로 저장되어 있기 때문에 저 부분은 그저 문자열을 참조하게 될 것입니다.
2번과 3번은 모두 우리가 원하는 값을 받을 수 있는데,

url 은 media 폴더 부터 시작하는 상대 경로를 의미하고,

path 는 절대경로를 나타냅니다.

둘 다 media 파일을 참조하기 때문에 이미지를 불러올 수 있습니다.

 

#urls.py

path('myprofile/profile/',myprofile.views.profile,name="profile"),

 

 

Media 파일 처리 순서

  1. views에서 HttpRequest.FILES 를 통해 파일이 전달
  2. views,form 로직을 통해 유효성 검사 수행
  3. ImageField(FileField)에 문자열로 경로를 저장
  4. settings.MEDIA_ROOT 경로에 파일을 저장 (원한다면 AWS등의 클라우드 스토리지에도 저장이 가능합니다)

댓글