1. 项目概述:告别数据线的局域网文件传输方案
每次在办公室需要给同事传文件时,你是不是还在满桌子找数据线?或者为了发个20MB的文档不得不登录微信?作为一个经常需要在多台设备间传输文件的开发者,我花了周末时间用FastAPI搭建了一个轻量级的局域网文件共享工具,现在可以完全摆脱数据线和第三方工具的束缚。
这个工具的核心功能非常简单:
- 在局域网内的任意设备间快速传输文件(支持大文件)
- 实时同步剪贴板内容(文字、图片)
- 零配置开箱即用
- 完全基于浏览器操作,无需安装客户端
实测下来,传输速度能达到局域网满速(千兆环境下约100MB/s),比微信文件传输快了近10倍。最棒的是,整个项目不到200行Python代码,用FastAPI实现起来异常简单。
2. 技术选型与架构设计
2.1 为什么选择FastAPI?
在评估了Flask、Django和FastAPI后,我最终选择了FastAPI作为后端框架,主要基于以下考虑:
-
性能优势:FastAPI基于Starlette和Pydantic,异步特性使其在处理文件上传下载时性能显著优于传统框架。在本地测试中,FastAPI处理文件请求的吞吐量是Flask的3倍左右。
-
开发效率:FastAPI的自动交互式文档、类型提示和请求验证让开发过程非常流畅。比如文件上传接口只需要这样定义:
python复制from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
- 现代特性:原生支持WebSocket,为后续实现实时通知功能(如下载完成提醒)留出了扩展空间。
2.2 整体架构设计
工具的核心架构分为三个层次:
-
前端层:纯HTML+JavaScript实现,利用浏览器File API处理文件选择,通过Fetch API与后端交互。这样就不需要开发原生客户端。
-
服务层:FastAPI提供RESTful接口处理文件上传下载,以及WebSocket服务管理剪贴板同步。
-
存储层:临时文件存储在内存中(小文件)或磁盘临时目录(大文件),定期清理过期文件。
关键的技术栈组合:
- 前端:Vanilla JS + FileReader API
- 后端:FastAPI + Uvicorn
- 传输协议:HTTP/WebSocket
- 文件处理:Python标准库shutil
3. 核心功能实现细节
3.1 文件传输服务实现
文件上传的核心逻辑在/upload端点实现,这里有几个关键点需要注意:
python复制from fastapi import FastAPI, UploadFile, File
from fastapi.staticfiles import StaticFiles
import os
import shutil
from pathlib import Path
app = FastAPI()
UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(exist_ok=True)
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
try:
# 安全处理文件名
safe_name = "".join(c for c in file.filename if c.isalnum() or c in "._- ")
dest = UPLOAD_DIR / safe_name
# 分块写入防止内存溢出
with dest.open("wb") as buffer:
while chunk := await file.read(1024 * 1024): # 1MB chunks
buffer.write(chunk)
return {"status": "success", "path": str(dest)}
except Exception as e:
return {"status": "error", "detail": str(e)}
重要提示:文件上传一定要实现分块读取,否则大文件会撑爆内存。我这里设置了1MB的块大小,实测在普通笔记本上传输2GB文件内存占用不超过50MB。
文件下载则更简单,直接使用FastAPI的FileResponse:
python复制from fastapi.responses import FileResponse
@app.get("/download/{filename}")
async def download_file(filename: str):
file_path = UPLOAD_DIR / filename
if file_path.exists():
return FileResponse(file_path)
return {"status": "error", "detail": "File not found"}
3.2 剪贴板同步实现
剪贴板同步使用WebSocket实现实时双向通信:
python复制from fastapi import WebSocket
import json
active_connections = []
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
active_connections.append(websocket)
try:
while True:
data = await websocket.receive_text()
# 广播给所有连接的客户端
for connection in active_connections:
if connection != websocket:
await connection.send_text(data)
except:
active_connections.remove(websocket)
前端需要配合实现剪贴板监听:
javascript复制const socket = new WebSocket(`ws://${location.host}/ws`);
// 监听剪贴板变化
document.addEventListener('copy', async (e) => {
const text = await navigator.clipboard.readText();
socket.send(JSON.stringify({type: 'clipboard', data: text}));
});
// 接收远程剪贴板
socket.onmessage = (event) => {
const msg = JSON.parse(event.data);
if(msg.type === 'clipboard') {
navigator.clipboard.writeText(msg.data);
}
};
4. 部署与使用指南
4.1 本地运行方式
- 安装依赖:
bash复制pip install fastapi uvicorn python-multipart
- 启动服务:
bash复制uvicorn main:app --reload --host 0.0.0.0 --port 8000
- 访问页面:
打开浏览器访问http://[你的IP]:8000,同一局域网下的设备都能访问这个地址。
4.2 高级部署选项
如果需要长期使用,建议:
- 使用系统服务方式运行(Linux示例):
bash复制# 创建服务文件 /etc/systemd/system/lan-share.service
[Unit]
Description=LAN File Share Service
After=network.target
[Service]
User=yourname
WorkingDirectory=/path/to/project
ExecStart=/usr/bin/uvicorn main:app --host 0.0.0.0 --port 8000
Restart=always
[Install]
WantedBy=multi-user.target
- 启用HTTPS(使用自签名证书):
bash复制openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
uvicorn main:app --host 0.0.0.0 --port 443 --ssl-keyfile key.pem --ssl-certfile cert.pem
5. 性能优化与安全考量
5.1 传输性能优化技巧
- 启用Gzip压缩:在FastAPI中启用Gzip可以显著提升文本类内容的传输速度:
python复制from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(GZipMiddleware, minimum_size=1000)
- 调整块大小:根据网络状况调整文件上传的块大小。千兆网络可以增加到4MB:
python复制while chunk := await file.read(4 * 1024 * 1024): # 4MB chunks
buffer.write(chunk)
- 并行传输:前端可以实现文件分片并行上传,大幅提升大文件传输速度。
5.2 安全防护措施
- 文件名消毒:防止路径遍历攻击
python复制from werkzeug.utils import secure_filename
safe_name = secure_filename(file.filename)
- 文件类型检查:限制可上传的文件类型
python复制ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
extension = file.filename.split('.')[-1].lower()
if extension not in ALLOWED_EXTENSIONS:
raise HTTPException(400, "File type not allowed")
- 访问控制:简单的密码保护
python复制from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
security = HTTPBasic()
def auth_user(credentials: HTTPBasicCredentials = Depends(security)):
correct_username = "user"
correct_password = "pass"
if not (credentials.username == correct_username and
credentials.password == correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect credentials",
headers={"WWW-Authenticate": "Basic"},
)
return True
@app.post("/upload")
async def upload_file(auth: bool = Depends(auth_user), file: UploadFile = File(...)):
...
6. 常见问题与解决方案
6.1 文件上传失败排查
问题现象:上传大文件时连接中断
可能原因及解决:
-
Nginx默认限制:如果使用Nginx反向代理,需要调整
client_max_body_sizenginx复制server { client_max_body_size 2G; } -
FastAPI超时设置:默认超时时间较短
python复制import uvicorn uvicorn.run(app, timeout_keep_alive=300) -
前端超时设置:Fetch API默认没有超时限制,但浏览器可能有
6.2 剪贴板同步不工作
问题现象:文本可以同步但图片不行
解决方案:
- 浏览器安全限制:需要HTTPS环境才能访问完整的剪贴板API
- 图片处理需要额外编码:
javascript复制// 读取图片剪贴板
const items = await navigator.clipboard.read();
for (const item of items) {
for (const type of item.types) {
if(type.startsWith('image/')) {
const blob = await item.getType(type);
// 转换为Base64传输
const reader = new FileReader();
reader.onload = () => {
socket.send(JSON.stringify({
type: 'clipboard-image',
data: reader.result,
mime: type
}));
};
reader.readAsDataURL(blob);
}
}
}
6.3 局域网设备无法访问
排查步骤:
- 检查服务是否绑定到
0.0.0.0而不仅是127.0.0.1 - 检查防火墙设置(以Windows为例):
powershell复制New-NetFirewallRule -DisplayName "LAN Share" -Direction Inbound -Protocol TCP -LocalPort 8000 -Action Allow - 确保所有设备在同一子网内,尝试互相ping测试
7. 功能扩展思路
基础版本实现后,可以考虑添加以下实用功能:
-
传输历史记录:使用SQLite保存传输记录
python复制from sqlite3 import connect conn = connect('transfers.db') conn.execute('''CREATE TABLE IF NOT EXISTS transfers (id INTEGER PRIMARY KEY, filename TEXT, size INTEGER, time TIMESTAMP)''') -
二维码快速连接:生成包含IP地址的二维码
python复制import qrcode def generate_qrcode(ip, port): url = f"http://{ip}:{port}" img = qrcode.make(url) img.save("static/qr.png") -
目录共享:直接共享整个目录
python复制from fastapi.staticfiles import StaticFiles app.mount("/shared", StaticFiles(directory="path/to/share"), name="shared") -
速度限制:防止单个用户占用全部带宽
python复制from slowapi import Limiter from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) app.state.limiter = limiter @app.post("/upload") @limiter.limit("10/minute") async def upload_file(request: Request, file: UploadFile = File(...)): ...
这个项目最让我惊喜的是,用如此简单的技术栈就实现了一个真正实用的生产力工具。现在我的团队已经完全抛弃了U盘和微信传文件的方式,特别是在需要传输大型开发包或设计素材时,效率提升非常明显。如果你也经常需要在局域网内传输文件,不妨花半小时试试这个方案
