边走边聊 Python 3.8:Chapter 22:个人网盘 / 文件同步工具
Chapter 22:个人网盘 / 文件同步工具数据需要流动,而工具让它自由。本章将带你构建一个可上传、下载、搜索、同步的个人网盘系统,理解文件分块、进度条、缓存等关键技术。你会体验到:当你能构建自己的网盘,你就能掌控自己的数字世界。“数据需要流动,工具让它自由。”第22篇我们把前面21篇的所有知识(Flask网页、SQLite数据库、文件操作、PyAutoGUI桌面自动化、OOXML报表、测试、CI/CD、PyInstaller打包)一次性整合,打造一个本地个人网盘/文件同步工具!支持拖拽上传、搜索、自动备份,完全运行在 Win7 本地,无需公网、无需服务器,双击 exe 即可使用。本篇全部基于Python 3.8.10 + Win7 旗舰版实测通过,先讲底层原理,再讲Win7 实战坑点,最后给出完整可直接运行的代码 + 文件分块上传 + 进度条,让你一键拥有自己的“私人百度网盘”!1. 底层原理(源码、协议、机制)Flask:基于WSGI 协议(Web Server Gateway Interface)。源码核心在werkzeug(werkzeug.serving和werkzeug.wsgi),它把 HTTP 请求转为 Python callable 函数。Win7 上直接用flask run启动内置开发服务器(Werkzeug),无需 Nginx/Apache。(上图:Flask + WSGI 架构流程)SQLite:纯文件型数据库(.db 文件)。底层是 C 语言的 B-Tree 索引 + 单文件存储(sqlite3.c),通过 Python 内置sqlite3模块直接操作,无需安装服务。支持事务、并发(Win7 下用 WAL 模式避免锁死)。(上图:SQLite 典型数据库结构)文件上传:HTTPmultipart/form-data协议。分块上传(Chunked Upload):浏览器用fetch+Blob.slice()把大文件切成小块(默认 1MB),每块单独 POST,服务器open(..., 'ab')追加写入,最后合并。避免 Win7 大文件上传超时/内存爆。拖拽上传 + 进度条:HTML5Drag and Drop API+XMLHttpRequest / Fetch+ProgressEvent。进度条实时读取e.loaded / e.total。自动备份:shutil.copytree+schedule(复用第18篇),或上传成功后立即复制到备份文件夹。2. Win7 特有实战坑点 库兼容性(提前避雷)Flask 版本锁定:Win7 + Python 3.8 推荐Flask==2.3.3(更高版本可能依赖新 asyncio)。pipinstallFlask==2.3.3端口 5000 被占用 / 防火墙:Win7 防火墙默认拦截。打包前先netstat -ano | find "5000"检查,运行时需以管理员身份运行exe。SQLite 文件锁:Win7 下多进程访问容易database is locked。解决:用PRAGMA journal_mode=WAL;开启 WAL 模式。中文文件名:Win7 默认 GBK,上传中文文件名必须filename.encode('utf-8')存储,读取时decode('utf-8')。大文件内存:Win7 内存有限,分块上传是必须的(单次上传 500MB 会卡死)。打包后路径:PyInstaller 打包后用sys._MEIPASS获取资源路径(第21篇技巧)。3. 深度:文件分块上传 + 进度条实现原理:前端:File →file.slice(start, end)→ 循环 fetch POST/upload_chunk后端:接收chunk_index、total_chunks、filename,追加写入临时文件,最后重命名。进度条:XMLHttpRequest的onprogress事件实时更新。4. 完整实战:本地个人网盘(拖拽上传 + 搜索 + 自动备份)项目结构(新建D:\MyNetDisk\):D:\MyNetDisk\ ├── app.py ├── static\ │ └── style.css ├── templates\ │ └── index.html ├── uploads\ ← 存放文件 ├── backup\ ← 自动备份 └── netdisk.db ← SQLite完整代码(app.py)—— Python 3.8 + Win7 直接复制可用:importosimportsqlite3importshutilfromdatetimeimportdatetimefromflaskimportFlask,request,jsonify,render_template,send_from_directoryimportscheduleimporttimeimportthreading app=Flask(__name__)UPLOAD_FOLDER='uploads'BACKUP_FOLDER='backup'CHUNK_SIZE=1024*1024# 1MB 分块app.config['UPLOAD_FOLDER']=UPLOAD_FOLDER os.makedirs(UPLOAD_FOLDER,exist_ok=True)os.makedirs(BACKUP_FOLDER,exist_ok=True)# 初始化 SQLitedefinit_db():conn=sqlite3.connect('netdisk.db')conn.execute('PRAGMA journal_mode=WAL;')# Win7 防锁conn.execute('''CREATE TABLE IF NOT EXISTS files ( id INTEGER PRIMARY KEY, filename TEXT UNIQUE, size INTEGER, upload_time TEXT )''')conn.commit()conn.close()init_db()# 自动备份线程(每小时备份一次)defbackup_task():defjob():ifos.path.exists(UPLOAD_FOLDER):shutil.copytree(UPLOAD_FOLDER,os.path.join(BACKUP_FOLDER,datetime.now().strftime('%Y%m%d_%H%M')),dirs_exist_ok=True)print("自动备份完成")schedule.every().hour.do(job)whileTrue:schedule.run_pending()time.sleep(60)threading.Thread(target=backup_task,daemon=True).start()@app.route('/')defindex():returnrender_template('index.html')@app.route('/upload_chunk',methods=['POST'])defupload_chunk