Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python CGI 프로그래밍으로 파일 업로드 기능 구현하는 방법

개요

웹에서 파일을 업로드하려면 HTML 폼의 enctype 속성을 반드시 multipart/form-data로 설정해야 합니다. 이렇게 설정해야 브라우저가 파일 데이터를 올바른 형식으로 서버에 전송할 수 있습니다. 또한 typefile<input> 태그를 사용하면 사용자가 로컬 파일을 선택할 수 있는 "파일 찾아보기" 버튼이 생성됩니다.

1단계: 파일 업로드용 HTML 폼 작성

아래는 파일을 선택하고 업로드할 수 있는 간단한 HTML 폼 예제입니다.

<html>
<body>
   <form enctype = "multipart/form-data"
                     action = "save_file.py" method = "post">
   <p>File: <input type = "file" name = "filename" /></p>
   <p><input type = "submit" value = "Upload" /></p>
   </form>
</body>
</html>

여기서 핵심 포인트는 다음과 같습니다.

  • enctype = "multipart/form-data": 파일 데이터 전송에 필수적인 인코딩 방식입니다.
  • action = "save_file.py": 폼 데이터가 제출되면 처리할 서버 측 파이썬 스크립트를 지정합니다.
  • method = "post": 파일 업로드에는 GET 방식이 아닌 POST 방식을 사용해야 합니다.

2단계: 실행 결과 확인

위 코드를 실행하면 브라우저에 다음과 같은 업로드 폼이 표시됩니다.

File: Choose file
Upload

사용자가 "Choose file" 버튼을 클릭해 파일을 선택하고 "Upload" 버튼을 누르면, 폼 데이터가 save_file.py 스크립트로 전달됩니다.

3단계: 파일 업로드를 처리하는 Python CGI 스크립트

다음은 업로드된 파일을 받아 서버에 저장하는 save_file.py 스크립트입니다.

#!/usr/bin/python
import cgi, os
import cgitb; cgitb.enable()
form = cgi.FieldStorage()
# 업로드된 파일 정보 가져오기
fileitem = form['filename']
# 파일이 실제로 업로드되었는지 확인
if fileitem.filename:
   # 디렉터리 순회 공격(directory traversal attack)을
   # 방지하기 위해 경로 부분을 제거하고 파일명만 추출
   fn = os.path.basename(fileitem.filename)
   open('/tmp/' + fn, 'wb').write(fileitem.file.read())
   message = 'The file "' + fn + '" was uploaded successfully'
 
else:
   message = 'No file was uploaded'
 
print """\
Content-Type: text/html\n
<html>
<body>
   <p>%s</p>
</body>
</html>
""" % (message,)

스크립트 동작 원리

  • cgi.FieldStorage()를 통해 폼으로 전송된 데이터를 읽어옵니다.
  • fileitem.filename이 존재하면 파일이 정상적으로 업로드된 것입니다.
  • os.path.basename()으로 경로를 제거하여 파일명만 추출함으로써 디렉터리 순회 공격 같은 보안 위협을 차단합니다.
  • 업로드된 파일은 바이너리 모드('wb')로 열어 /tmp/ 디렉터리에 저장됩니다.
  • 처리 결과 메시지를 HTML 형태로 출력하여 사용자에게 성공 여부를 알려줍니다.

운영체제별 주의 사항

위 스크립트를 Unix/Linux 환경에서 실행한다면, Windows에서 만들어진 파일 경로의 구분자(역슬래시 \\)를 처리하기 위해 다음과 같이 코드를 수정해야 합니다.

fn = os.path.basename(fileitem.filename.replace("\\", "/" ))

반대로 Windows 머신에서 실행하는 경우에는 수정 없이 기존 open() 문이 정상적으로 동작합니다.