我们将使用 Python 最流行的 Web 框架 Flask 来实现,因为它轻量、简单且非常适合这类任务,我也会提供一个使用 FastAPI 的示例,因为它的性能更好,并且对异步支持更友好。

使用 Flask (简单、易上手)
Flask 是一个微框架,非常适合快速构建 Web 应用和 API。
安装 Flask
你需要安装 Flask,如果你还没有安装,可以通过 pip 来安装:
pip install Flask
创建上传目录
在你的项目文件夹下,创建一个用于存储上传文件的目录,uploads。
/my_project
├── app.py
└── uploads/
确保 uploads 目录存在,并且你的 Web 服务器进程对该目录有写入权限。

编写 Flask Web 服务代码 (app.py)
下面是一个完整的、可运行的 Flask 应用示例,它包含一个用于上传文件的 HTML 表单和一个处理文件上传的后端 API。
# app.py
from flask import Flask, request, render_template, redirect, url_for
import os
# 创建 Flask 应用实例
app = Flask(__name__)
# 配置上传文件的目录
# 使用绝对路径或确保相对于脚本的路径是正确的
UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'uploads')
# 确保上传目录存在
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)
# 配置允许的文件扩展名
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
# Flask 应用配置
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 限制上传文件大小为 16MB
# 辅助函数:检查文件扩展名是否合法
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
# 主页 - 显示上传表单
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 检查是否有文件在请求中
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# 如果用户没有选择文件,浏览器也会提交一个空的文件部分
if file.filename == '':
flash('No selected file')
return redirect(request.url)
# 如果文件合法,则保存
if file and allowed_file(file.filename):
# 为了防止文件名冲突,可以生成一个安全的文件名
# 或者直接使用原始文件名
filename = file.filename
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('upload_file', filename=filename))
# GET 请求时,显示上传页面
# 这里我们简单返回一个提示,实际项目中可以渲染一个HTML模板
return '''
<!doctype html>Upload new File</title>
<h1>Upload new File</h1>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit value=Upload>
</form>
'''
# 用于显示上传成功信息的路由 (可选)
@app.route('/uploads/<filename>')
def uploaded_file(filename):
return f'File {filename} has been uploaded successfully!'
if __name__ == '__main__':
# 在开发环境中运行,生产环境应使用 Gunicorn 或 uWSGI
app.run(debug=True, host='0.0.0.0', port=5000)
运行服务
在终端中,导航到你的项目目录 (my_project),然后运行:
python app.py
你会看到类似下面的输出:
* Serving Flask app 'app'
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: ...
测试上传
- 打开浏览器,访问
http://127.0.0.1:5000。 - 你会看到一个文件选择框和一个“Upload”按钮。
- 选择一个文件(
test.jpg),然后点击上传。 - 文件会被保存到你项目下的
uploads文件夹中。 - 页面会重定向,并显示上传成功的消息。
使用 FastAPI (现代、高性能、自动文档)
FastAPI 是一个现代、快速的(高性能)Web 框架,用于构建 API,它基于 Starlette 和 Pydantic,并且自动生成交互式 API 文档。
安装 FastAPI 和 Uvicorn
pip install "fastapi[all]"
[all] 会自动安装 uvicorn(ASGI 服务器)和 python-multipart(用于处理表单数据)。
创建上传目录
和 Flask 一样,创建一个 uploads 目录。
编写 FastAPI 服务代码 (main.py)
# main.py
import os
import shutil
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import HTMLResponse
app = FastAPI()
# 配置上传目录
UPLOAD_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "uploads")
if not os.path.exists(UPLOAD_DIRECTORY):
os.makedirs(UPLOAD_DIRECTORY)
# 定义允许的文件类型
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
def allowed_file(filename: str):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
"""
接收单个文件上传
"""
# 检查文件类型
if not allowed_file(file.filename):
raise HTTPException(status_code=400, detail="File type not allowed")
# 构建文件保存路径
file_location = os.path.join(UPLOAD_DIRECTORY, file.filename)
# 使用 async 将文件内容写入磁盘
try:
with open(file_location, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Could not save file: {e}")
return {"info": f"file '{file.filename}' saved at '{file_location}'"}
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile] = File(...)):
"""
接收多个文件上传
"""
info = []
for file in files:
if not allowed_file(file.filename):
# 可以选择跳过或报错,这里我们跳过并记录
info.append(f"Skipped invalid file: {file.filename}")
continue
file_location = os.path.join(UPLOAD_DIRECTORY, file.filename)
try:
with open(file_location, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
info.append(f"file '{file.filename}' saved at '{file_location}'")
except Exception as e:
info.append(f"Could not save file {file.filename}: {e}")
return {"info": info}
@app.get("/", response_class=HTMLResponse)
async def main():
"""
提供一个简单的HTML上传页面
"""
return """
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h1>Upload a file</h1>
<form action="/uploadfile/" enctype="multipart/form-data" method="post">
<input name="file" type="file">
<input type="submit">
</form>
<h2>Or upload multiple files</h2>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
</html>
"""
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
运行服务
uvicorn main:app --reload
--reload 参数会在代码变更时自动重启服务器,方便开发。
测试上传和 API 文档
- API 文档: 打开浏览器访问
http://127.0.0.1:8000/docs,你会看到由 Swagger UI 生成的交互式 API 文档。 - HTML 上传页面: 访问
http://127.0.0.1:8000/,你可以像使用 Flask 示例一样通过网页上传文件。 - API 上传: 你也可以使用
curl或 Postman 等工具
