Python/Django

장고(django)에서 이미지 업로드 필드 추가 없이

noodle-dev 2020. 4. 24. 10:45

장고로 이미지 업로드 하는 방법을 찾아보면 대부분은 모델에 이미지 필드를 추가하고 폼을 생성하여 사용자에게 사진을 받는 방식으로 진행된다. 혹시나 그 방법이 궁금해서 들어온 방문자가 있을까 해당 방법과 동시에 필자가 이번에 찾아본 필드 추가 없이 업로드를 하는 방법을 정리할 것이다.

 

이미지 필드로 업로드

def avatar_path(instance, filename):
    return 'avatar/u/'+instance.user.username+ '/' + randstr(4) + '.' + filename.split('.')[-1]

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    avatar = models.ImageField(blank=True, upload_to=avatar_path)

 


만일 사용자의 대표 아바타 이미지를 추가한다고 가정하면 위와같이 이미지 필드를 추가하고 아래와 같이
 forms.py에서 업로드할 폼을 만들어 준다.

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('avatar',)

 

이제 위 폼을 views.py를 통해서 사용자에게 폼을 전달해주고 파일을 받아올 것이다.

if request.method == 'POST':
    if hasattr(user, 'profile'):
        profile = user.profile
        p_form = ProfileForm(request.POST, request.FILES, instance=profile)
    else:
        p_form = ProfileForm(request.POST, request.FILES)
    if p_form.is_valid():
        profile = p_form.save(commit=False)
        profile.user = user
        profile.save()
        render_args['message'] = '성공적으로 변경되었습니다.'
if hasattr(user, 'profile'):
    profile = user.profile
    p_form = ProfileForm(instance=profile)
else:
    p_form = ProfileForm()
render_args['p_form'] = p_form

 

 

 

뷰에서 위와같이 처리해주면 끝난다! 이미지 필드에 저장된 이미지의 경로가 들어있는 것을 확인할 수 있다.

 

AJAX로 이미지 업로드

단순한 이미지 업로드(AJAX)와 같은 경우 PHP를 활용해서 업로드했었다. 그러다 오늘 PHP 의존도를 낮추기 위해 해당 기능을 파이썬으로 변경하고자 하였다. 아래의 PHP코드를 Python으로 변환하였다.

<?php
include "../randstr/randstr.php";

$uploads_url = 'https://MY_DOMAIN.COM/image';
$allowed_ext = array('jpg', 'jpeg', 'png', 'gif');

$uploads_dir = '../../static/image';
$name = $_FILES['image']['name'];
$error = $_FILES['image']['error'];
$ext = strtolower(array_pop(explode('.', $name)));

if(!in_array($ext, $allowed_ext)) {
	echo "허용되지 않는 확장자입니다.";
	exit;
}

if( $error != UPLOAD_ERR_OK ) {
	switch( $error ) {
		case UPLOAD_ERR_INI_SIZE:
		case UPLOAD_ERR_FORM_SIZE:
			echo "파일이 너무 큽니다. ($error)";
			break;
		case UPLOAD_ERR_NO_FILE:
			echo "파일이 첨부되지 않았습니다. ($error)";
			break;
		default:
			echo "파일이 제대로 업로드되지 않았습니다. ($error)";
	}
	exit;
}

$new_name = randstr(20);

if(move_uploaded_file($_FILES['image']['tmp_name'], $uploads_dir.'/'.$new_name.'.'.$ext)){
    echo $uploads_url.'/'.$new_name.'.'.$ext;
} else {
    echo "이미지 업로드를 실패하였습니다.";
}
?> 

 

PHP의 경우에는 사용자에게 POST받은 순간부터 이미지 업로드가 되는 것 같다. 뭐 당연한 얘기인가… move_uploaded_file로 원하는 위치로 옮기는 것이 핵심 코드며 그 전 코드들은 예외를 처리하기 위해서 작성된 코드다.

@csrf_exempt
def image_upload(request):
    if request.method == 'POST':
        if request.FILES['image']:
            allowed_ext = ['jpg', 'jpeg', 'png', 'gif']
            
            image = request.FILES['image']
            ext = str(request.FILES['image']).split('.')[-1].lower()

            if not ext in allowed_ext:
                return HttpResponse('허용된 확장자가 아닙니다.')
                
            upload_path = 'static/image/'

            file_name = randstr(20)
            with open(upload_path + '/' + file_name +'.'+ ext, 'wb+') as destination:
                for chunk in image.chunks():
                    destination.write(chunk)
            
            return HttpResponse('https://MY_DOMAIN.COM/image/' + '/' + file_name +'.'+ ext)
        else:
            return HttpResponse('이미지 파일이 아닙니다.')
    else:
        raise Http404

 

파이썬의 경우에는 request.FILES를 통해서 파일이 넘어온다. 이걸 원하는 경로에 데이터 기록시켰다. PHP도 내부적으론 이와같은 일이 발생하는 거라고 추측된다. @csrf_exempt은 해당 메서드가 모델로 부터 폼을 전달받는게 아니므로 csrf 보안을 적용시킬 수가 없었기에 해당 메서드에 한해서 보안을 해제시켰다.

 

출처: https://baejino.com/programing/django/how-to-image-upload