개요
웹에서 파일을 업로드하려면 HTML 폼의 enctype 속성을 반드시 multipart/form-data로 설정해야 합니다. 이렇게 설정해야 브라우저가 파일 데이터를 올바른 형식으로 서버에 전송할 수 있습니다. 또한 type이 file인 <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() 문이 정상적으로 동작합니다.