HOWTO · Flask
Flask를 사용하여 파일 다운로드
이 설명을 통해 Flask의 send_file 함수를 사용하여 파일을 첨부 파일로 다운로드하는 다운로드 가능한 링크를 만드는 방법을 배웁니다.
이 페이지의 내용
Flask에서 링크를 눌렀을 때 파일을 다운로드하게 하려면 send_file(path, as_attachment=True)를 반환하는 라우트를 정의한 다음, 템플릿의 링크가 url_for로 그 라우트를 가리키게 하면 됩니다. 라우트가 사용하는 경로에 파일이 있어야 합니다.
Flask 다운로드 라우트에서 파일 반환하기
send_file은 파일을 담은 응답을 만듭니다. 뷰 함수는 이 응답을 반환해야 합니다. return 없이 send_file만 호출하면 Flask는 의도한 응답을 받지 못합니다. as_attachment=True를 지정하면 Flask가 파일을 첨부 파일로 보내므로, 브라우저는 파일을 페이지로 표시하는 대신 다운로드하도록 안내합니다.
템플릿에서 다운로드 라우트로 연결하기
라우트의 엔드포인트 이름을 url_for에 전달하세요. 기본적으로 Flask는 뷰 함수 이름을 엔드포인트로 사용하므로 아래의 링크와 함수는 모두 download_file을 사용합니다. URL 경로(/download)와 엔드포인트 이름은 서로 다릅니다. 템플릿을 렌더링할 때 url_for는 엔드포인트 이름을 조회합니다. GET 링크를 사용해도 철자가 틀렸거나 등록되지 않은 엔드포인트 문제는 해결되지 않습니다.
템플릿은 app.py와 같은 위치에 있는 templates 폴더에 넣어야 합니다.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
<h2>Download the sample file</h2>
<p><a href="{{ url_for('download_file') }}">Download</a></p>
안정적인 파일 경로와 첨부 파일 설정 사용하기
app.root_path를 기준으로 경로를 만들면 셸의 현재 작업 디렉터리에 의존하지 않고 애플리케이션 디렉터리를 기준으로 파일을 찾을 수 있습니다. 이 로컬 예제는 app.py 옆에 작은 텍스트 파일을 만듭니다. 실제로 제공하려는 파일의 경로에 맞게 바꾸세요. URL이나 폼에서 임의의 파일 시스템 경로를 받아 그대로 send_file에 전달하지 마세요.
첨부 파일 이름은 기본적으로 경로에서 가져오므로, 이 예제에서는 파일이 sample.txt라는 이름으로 제공됩니다. 저장된 이름과 다운로드할 때 표시할 이름을 다르게 하려면 send_file에 원하는 download_name 값을 전달하세요. 파일 경로는 직접 관리하고 제공하기 전에 파일이 있는지 확인하세요. 방문자가 제공한 경로보다 신뢰할 수 있는 애플리케이션 설정으로 만든 경로가 관리하기 쉽습니다.
다음 내용을 app.py로 저장하세요. 애플리케이션 옆에 템플릿과 샘플 파일을 만든 뒤, 서버 프로세스를 시작하지 않고 Flask 테스트 클라이언트로 요청을 보냅니다.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
from pathlib import Path
from flask import Flask, render_template, send_file
app = Flask(__name__)
template_dir = Path(app.root_path) / "templates"
template_dir.mkdir(exist_ok=True)
(template_dir / "index.html").write_text(
'<p><a href="{{ url_for(\'download_file\') }}">Download</a></p>',
encoding="utf-8",
)
SAMPLE_FILE = Path(app.root_path) / "sample.txt"
SAMPLE_FILE.write_text("A file served by Flask.\n", encoding="utf-8")
@app.route("/")
def index():
return render_template("index.html")
@app.route("/download")
def download_file():
return send_file(SAMPLE_FILE, as_attachment=True)
if __name__ == "__main__":
with app.test_client() as client:
print(client.get("/").get_data(as_text=True).strip())
response = client.get("/download")
print(response.status_code)
print(response.headers["Content-Disposition"])
print(response.get_data(as_text=True), end="")
템플릿은 download_file을 /download로 연결하고, 테스트 클라이언트도 해당 라우트에 요청을 보냅니다. 스크립트를 실행하면 렌더링된 링크, 응답 상태, 첨부 파일 헤더, 파일 내용이 출력됩니다. 첨부 파일 헤더를 확인하면 브라우저에 응답을 다운로드하도록 안내했는지 알 수 있고, 본문을 확인하면 선택한 파일이 제공되었는지 알 수 있습니다.
전체 예제 실행 및 확인하기
프로젝트 환경에 Flask가 설치되어 있지 않다면 설치한 다음 python app.py를 실행해 렌더링된 링크와 다운로드 응답을 로컬에서 확인하세요. 브라우저에서 같은 라우트를 시험하려면 flask --app app run으로 Flask 개발 서버를 시작하고, http://127.0.0.1:5000/을 연 뒤 Download를 선택하세요. 브라우저가 sample.txt를 다운로드해야 합니다. /download에 직접 접속해도 같은 첨부 파일이 제공됩니다. 서버를 중지하려면 Ctrl+C를 누르세요. 디버그 모드는 로컬 개발에서만 사용하세요. 외부에서 접속할 수 있는 서버에서는 대화형 디버거를 활성화하지 마세요.