[Django] 서버로 파일 업로드하는 API 만들기
오늘 할 것
이전시간에 우리는 모달을 이용해 이미지를 업로드하는 html 윤곽만 잡았다.
이제 사진과 함게 글을 Django 서버에 올리고 그것을 띄어주는 것을 만들 것이다.
우리는 이미지, 글 ,사용자ID 등 데이터를 Django 서버에 올려야한다.
view.py 에서 데이터를 모아서 데이터베이스 Feed 테이블에 저장해야한다.
프론트에서 서버로 올리기 위해 Ajax(Asynchronous JavaScript and XML) 를 사용한다.
Ajax : 빠르게 동작하는 동적인 웹 페이지를 만들기 위한 개발기법, 웹 페이지 전체를 다시 로딩하지 않고 일부분만 갱신한다.
동기 : 요청을 하고 응답이 끝날 때 까지 기다리는 것
비동기 : 요청하고 응답이 안와도 그냥 할 것 함
우리는 이제까지 url 로 get 을 호출해서 html 을 띄어주었다.
이때 render(request, "...html", data)를 이용했는데 이건 화면 전체를 바꾸어 주는 기능이다.
하지만 우리는 전체 페이지를 바꿀 필요가 없기 때문에 Ajax 를 사용한다.
dragOver 이미지 드래그 앤 드롭 사용
<div class="img_upload_space" style="background-color: darkgray; width:50%; min-height:540px; margin: 10px">
dd</div>
...
<script>
// 이미지 드래그 앤 드롭
$('.img_upload_space')
.on("dragover", dragOver)
.on("dragleave", dragOver)
.on("drop", uploadFiles);
function dragOver(e) {
e.stopPropagation();
e.preventDefault();
if (e.type == "dragover") {
$(e.target).css({
"background-color": "black",
"outline-offset": "-20px"});
{% comment %} alert('hello!') {% endcomment %}
} else {
$(e.target).css({
"background-color": "gray",
"outline-offset": "-10px"});
}
}
</script>
이미지를 끌고 왔을 때 화면이 검게 변하도록 해주기
drop시 uploadFiles(e) 실행 + 화면에 띄어주기
<div class="img_upload_space" style="background-color: darkgray; width:50%; min-height:540px; margin: 10px">
dd</div>
...
<script>
function uploadFiles(e) {
e.stopPropagation();
e.preventDefault();
dragOver(e); //1
e.dataTransfer = e.originalEvent.dataTransfer; //2
files = e.target.files || e.dataTransfer.files;
if (files.length > 1) {
alert('하나만 올려라.');
return;
}
if (files[0].type.match(/image.*/)) { // 이미지 업로드 됐을 때 띄우기
$('#first_modal').css({
display: 'none'
});
$('.img_upload_space').css({
"background-image": "url(" + window
.URL
.createObjectURL(files[0]) + ")",
"outline": "none",
"background-size": "100%",
"background-repeat": "no-repeat",
"background-position": "center"
});
$('#second_modal').css({
display: 'flex'
});
} else {
alert('이미지가 아닙니다.');
return;
}
}
</script>
공유버튼을 눌렀을 때 이미지파일과 데이터를 서버로 보내주기
ajax 를 이용해 비동기로 데이터를 보내준다. 이후에 이걸 띄어주어야한다.
url : content/upload 로 들어가서 POST 로 서버로 데이터를 전송해준다.
<button id="feed_create_button" type="button" class="btn btn-primary">
공유하기
</button>
...
<script>
//공유 버튼 눌렀을 때
$('#feed_create_button').click(function () {
alert('공유 누름!' + files[0]);
let file = files[0];
let image = files[0].name;
let cotent = $('#input_feed_content').val();
let user_id = 'emocode_admin';
let profile_image = "{% static 'images/sogangcharacter.webp' %}";
// json 데이터만 넘기냐
// image 같은 데이터도 같이 넘기기 위해
let fd = new FormData();
fd.append('file', file);
fd.append('image', image);
fd.append('content', cotent);
fd.append('user_id', user_id);
fd.append('profile_image', profile_image);
$.ajax({
url: "content/upload",
data: fd,
method: "POST",
processData: false,
contentType: false,
success: function (data) {
console.log("성공");
},
error: function (request, status, error) {
console.log('에러');
},
complete: function () {
console.log('완료');
location.replace("");
}
})
</script>
이제 jinstagram/urls.py 에 url 추가해주고
urlpatterns = [
...
path('content/', include('content.urls')),
...
]
content/urls.py 에 content/upload/ 로 가면 업로드를 시행하는 것 같음?
urlpatterns = [
path('upload', UploadFeed.as_view())
]
# 파일 나중에 조회하는 목적
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
content/views.py 에 두번째 모달에서 받은 이미지, 데이터를 서버에서 받아 저장해 Feed 로 보내는 코드
main.html 에서 Feed 정보를 토대로 게시물을 띄우기 때문에 생성한 게시물이 띄어진다.
# 두번째 모달에서 받은 이미지, 데이터 받기
class UploadFeed(APIView):
def post(self, request):
# 파일 불러오기
file = request.FILES['file']
# 저장 준비
uuid_name= uuid4().hex # 랜덤으로 고유한 값으로 저장, 한글 에러 날수도
save_path = os.path.join(MEDIA_ROOT, uuid_name)
with open(save_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
image = uuid_name
content = request.data.get('content')
user_id = request.data.get('user_id')
profile_image = request.data.get('profile_image')
Feed.objects.create(image=image, content=content, user_id=user_id, profile_image=profile_image, like_count=0)
return Response(status=200)
여기서 우리는 파일의 대체 이미지 이름인 uuid_name 으로 저장을 했기 때문에
이걸 main.html 에서 띄어주려면 https://127.0.0.1:8000/media/uuid_name 으로 잘 불러와야 한다.
그 코드를 main.html 에 적용하면
<!-- 피드 박스 -->
{% for feed in feeds %}
<div class="border feed_box">
<!-- 프로필 -->
<div style="
display: flex;
flex-direction: row;
align-items: center;
margin: 10px;
">
<!-- 프로필사진 -->
<div class="box" style="display:flex;">
<img class="profile" src="{{feed.profile_image}}" style="background: grey"/>
</div>
<!-- 사용자명 -->
<div style="margin-left: 10px">{{feed.user_id}}</div>
</div>
<!-- 이미지 -->
<div>
<img src="{% get_media_prefix %}{{feed.image}}" style="display:block;width: 100%;height:auto"/>
</div>
<!-- 아이콘들 -->
<div style="
display: flex;
justify-content: space-between;
margin: 10px 20px;
">
<div>
<span class="material-icons-outlined">
favorite_border
</span>
<span class="material-icons-outlined">
mode_comment
</span>
<span class="material-icons-outlined">
send
</span>
</div>
<div>
<span class="material-icons-outlined">
bookmark_border
</span>
</div>
</div>
<!-- 좋아요 -->
<div style="text-align: left; margin: 0 20px">
춘식이
<b>외
{{feed.like_count}}</b>이 좋아합니다
</div>
<!-- 캡션 -->
<div style="text-align: left; margin: 0 20px">
<b>{{feed.user_id}}</b>
{{feed.content}}
</div>
<!-- 댓글 -->
<div style="text-align: left; margin: 0 20px; font-size: 14px">
<b>aaa</b>
나도 놀러가고싶다
</div>
<div style="text-align: left; margin: 0 20px; font-size: 14px">
<b>bbb</b>
나도~
</div>
<!-- 댓글 입력창 -->
<input type="text" class="form-control shadow-none" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="댓글 달기.." style="font-size: 14px"/>
</div>
{% endfor %}
<!-- 여기까지 피드 박스 -->
</div>
** <head> 부분에 아래 추가하기
<!-- connect static -->
{% load static %}
여기까지 유투브 06:02:10 내용