
把一个大文件从电脑发到手机你有没有经历过这种折腾开微信、选文件、等压缩、再存到手机或者开 QQ、登录、传文件结果提示文件过大又或者临时拉根数据线电脑手机来回插拔。我后来找到一条最快路径用 Python 自带的一个模块在局域网里启动一台轻量文件服务器手机浏览器打开一个地址直接下载速度基本跑满路由器的局域网带宽。这个操作不装额外软件、不登录账号、不压缩画质省掉一堆破事。这就是我要讲的 Python LAN Server 极简玩法。它适合临时共享文件、内网传资料、给同事演示静态页面也适合作为学习后端编程的第一块跳板。你将学会一行命令启动静态文件服务自己写 50 行代码做上传/下载再用 Flask 搭一个能被局域网内手机直接访问的小接口。这篇内容不预设你会任何框架只要电脑上能运行 Python 就行。1. 先想清楚你要的到底是文件共享还是 Web 服务1.1 局域网服务器最常见的四种形态不用一上来就买服务器一个普通笔记本就够。我见过最多的需求有四类文件下载把电脑上的安装包、几百张照片、小说合集放到浏览器里手机上一点就保存。文件上传零散文件从手机丢到电脑不经过聊天软件不用数据线。页面展示把前端写好的静态页面拖动到目录里给同事的手机演示效果比截图强。接口调试电脑上写了个接口手机 App 或者小程序需要访问http://192.168.x.x:5000/api/xxx联调而不是只能本机 curl。用一个表格列一下这四类的方向需要做的事典型场景极简工具静态文件下载/展示传大文件、看 HTML 页面http.server上传文件到电脑手机照片批量备份、收作业自写 HandlerJSON 接口给小程序/前端联调Flask公网访问在外网访问家里文件建议别用这个当主力1.2 为什么选 Python而不是 Nginx、SMB 或局域网聊天工具如果只是临时共享文件很多路由器自带 SMBWindows 也能开启文件共享。但 SMB 在手机端并不总是顺畅iOS 的“文件”App 访问 SMB 要填一堆账号安卓上也要装第三方文件管理器。Python 起一个 HTTP 服务客户端只需要浏览器这个门槛低很多。“极简”的核心优势有两个一个是零依赖Python 标准库自带http.server不需要编译、不需要配置文件另一个是跨平台Windows、macOS、Linux 的体验一致同一个命令在校园网、办公室、家里都能用。当然如果要并发几千人访问这根本不是它该干的事那才需要 Nginx。我划定界限是临时、小规模、自己人能用的场景Python 足够长期、正式、开放给陌生人的场景请继续看 Nginx 的文档。我为什么不推荐用网盘因为网盘要上传等待慢还有隐私风险局域网文件服务不过外网数据只在路由器里走一圈。如果你只是想在同一个 WiFi 下从电脑给手机传资料网盘其实是绕远路。1.3 选型内置 http.server 还是 Flask很多人会纠结既然要写接口是不是直接上 Flask我的建议是先看需求。只要在浏览器里看到文件列表、点开就能下http.server就够了零安装。等到需要做动态逻辑比如“根据参数返回不同结果”“处理表单上传”“提供 JSON API”再升级 Flask。Flask 也是轻量级但至少需要pip install flask在没网的内网环境里可能折腾。另一个维度是学习成本。新手拿http.server练手能趁早理解 HTTP 请求、端口、IP 这些概念直接上 Flask 很容易卡在路由和装饰器上。这篇文章先讲标准库再给一个 Flask 例子两条路你都见过了后面踩坑心里有底。2. 环境准备能跑 Python 就成功了一半2.1 确认 Python 和 pip 是否可用在终端里敲两行命令python --version pip --version能打印版本号说明环境没问题。Windows 如果提示python 不是内部或外部命令多半是安装时没有勾选Add Python to PATH。最省事的解决方式不是手动去改环境变量而是重新运行 Python 安装包在第一个页面把最底下的Add Python to PATH勾上再点 Install Now。改完重开终端就生效。提示macOS 和 Linux 上经常需要输入python3而不是python因为系统里可能同时有多个 Python 版本。如果你输入python报错先试python3。pip 是 Python 的包管理器后面装 Flask 要用的。可以顺手把 pip 升级到最新版python -m pip install --upgrade pip如果网络源比较慢可以临时换国内镜像源例如清华源。不过极简入门阶段先不必在意这个。2.2 虚拟环境极简场景可以直接跳过但要知道怎么用很多教程逼你创建虚拟环境因为虚拟环境能隔离项目的第三方依赖避免污染全局 Python。但如果你的目标只是python -m http.server 8000根本不需要虚拟环境标准库直接就能跑。什么时候需要后面用 Flask 了每个项目装的包版本可能不同这时才建议建一个。创建和激活很简单python -m venv venv # Windows 激活 venv\Scripts\activate # macOS/Linux 激活 source venv/bin/activate激活后终端前缀会变成(venv)这时再装 Flask 就只装在这个环境里。Windows 用户在 PowerShell 里激活如果提示执行策略可以改用 CMD或者用venv\Scripts\Activate.ps1被组策略拦这一步我见过不少新手中招最容易的绕过办法是直接改用cmd.exe再运行venv\Scripts\activate.bat反而顺手。2.3 最容易卡住的端口占用和访问权限http.server默认端口是 8000但 8000 在很多开发环境里也经常被占用。启动时如果立刻报Address already in use说明该端口已经被别的进程占了。解决方式换个端口比如python -m http.server 8080或9876。还有个更隐蔽的坑防火墙。Windows 第一次运行python监听端口时系统会弹窗询问“是否允许 Python 通过防火墙”如果点了取消局域网其他设备就访问不到。macOS 也有类似的防火墙设置需要在“系统设置 → 网络 → 防火墙”里允许 Python 接受传入连接。Linux 服务器如果开了防火墙需要放行端口例如sudo ufw allow 8000/tcp这些我称之为“起服务三件套”端口不冲突、防火墙放行、同一网段。后面专门有一节讲排查。3. 一行命令启动局域网文件服务器3.1python -m http.server的完整用法假设你有一个目录D:\share里面有一堆图片和 PDF想快速分享给局域网内的手机打开终端cd D:\share python -m http.server 8000然后浏览器访问http://localhost:8000你会看到这个目录的文件列表。如果在本机上访问没问题就说明服务已经起来了。常用参数其实就几个参数作用示例8000监听端口可换其他端口python -m http.server 9999--directory指定要共享的目录python -m http.server 8000 --directory /path/to/folder--bind绑定地址默认所有网卡python -m http.server 8000 --bind 0.0.0.0很多人会忽略--bind。默认情况下http.server会监听所有网络接口也就是说局域网 IP 也能访问。但如果你在某种受限环境下服务只监听了127.0.0.1那么别人无论如何都访问不了。见到这种情况检查一下是不是配置上加了--bind 127.0.0.1。从入门角度默认啥都不用加直连即可。--directory很实用。你不需要先cd到目标目录可以直接指向任意路径python -m http.server 8000 --directory C:\Users\me\Desktop\下载注意这里的http.server是一个标准库模块因此不会出现在任何虚拟环境中直接用系统 Python 跑即可。如果想把这行命令交给不熟悉命令行的同事用可以把它写进一个.bat脚本或.sh脚本双击运行后面再讲。3.2 手机和别的电脑怎么访问先查 IP问题来了服务起来了但手机浏览器输入 localhost 肯定不对。这是初学者最容易懵的地方。localhost 是“本机”的别名手机访问电脑时得填电脑的局域网 IP 地址。Windows 查看 IPipconfig找到“无线局域网适配器 WLAN”或“以太网适配器”下的IPv4 地址常见是192.168.x.x或10.x.x.x。macOS/Linux 可以用ifconfig # 或者 ip addr然后手机连接同一个 WiFi在浏览器输入http://192.168.1.123:8000这时应该能看到和电脑浏览器一样的文件列表。点一下文件就能下载。我在实测中遇到过一个问题路由器开启了“AP 隔离”手机和电脑虽然连的同一个 WiFi但两者之间不可达。这种一般在公司网络、餐厅 WiFi、校园网红访网络里出现。症状是电脑上 localhost 能访问手机输入 IP 却一直转圈。解决办法是换一个 WiFi或者让路由器管理员关闭 AP 隔离。这个知识点不属于 Python但在 LAN Server 场景里非常影响体验。3.3 访问不了时按这个顺序排查遇到局域网访问不了不要瞎猜按下面的顺序走症状可能原因处理方式电脑 localhost 都访问不了服务没起来 / 端口被占用启动时看终端报错换端口电脑能访问手机不行手机和电脑不在同一网段 / AP 隔离 / 防火墙先ping 电脑IP再检查防火墙规则输入地址后浏览器报“拒绝连接”端口没监听 / 服务已退出回终端看 CtrlC 是否误按下载一半断开网络不稳定 / 服务端关闭换 5G WiFi 频段或改用有线核心思路是先在本机验证服务再在局域网内验证网络。本机能访问只能说明服务正常手机访问不了就是网络或防火墙的问题。我每次排错都会先ping一下电脑 IP能通则说明二层网络是通的然后再检查端口。还有一个小细节手机浏览器访问时不要加https://因为http.server默认是 http 服务。别自作主张补 443 端口直接输IP:端口即可。4. 从网页下载升级成网页上传写一个局域网传文件小工具4.1 为什么不自带上传功能内置的http.server只能查看和下载不能上传。日常场景中手机往电脑传照片更频繁。有人会装第三方工具但极简路线下我们可以用标准库写一个不到 80 行的上传服务器。我需要先说明实现思路。HTTP 上传一般用multipart/form-data浏览器会用一段随机字符串当 boundary把表单里的字段和文件内容切分好POST 给服务器。我们可以硬解析这个格式不依赖任何第三方库。如果你觉得解析 multipart 太底层也可以去用 Flask不过在http.server里自己解析一次对理解 HTTP 很有帮助。下面的代码侧重可读性不考虑安全边界仅限可信局域网内使用。4.2 基于标准库的上传服务器源码import os import re import urllib.parse from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer UPLOAD_DIR uploads os.makedirs(UPLOAD_DIR, exist_okTrue) class UploadHandler(BaseHTTPRequestHandler): def do_GET(self): if self.path / or self.path.startswith(/?): html !doctype html html langzh headmeta charsetutf-8title局域网传文件/title/head body h2选择文件传到电脑/h2 form methodpost enctypemultipart/form-data input typefile namefile multiple button typesubmit上传/button /form /body /html self.send_response(200) self.send_header(Content-Type, text/html; charsetutf-8) self.end_headers() self.wfile.write(html.encode(utf-8)) else: self.send_response(404) self.end_headers() def do_POST(self): length int(self.headers.get(Content-Length, 0)) body self.rfile.read(length) content_type self.headers.get(Content-Type, ) boundary_match re.search(rboundary(.*)$, content_type) if not boundary_match: self.send_response(400) self.end_headers() return boundary boundary_match.group(1).strip().strip() sections body.split((-- boundary).encode()) saved [] for sec in sections: if len(sec) 4: continue if sec.startswith(b--): # 最后一个结束标志 continue header, _, content sec.partition(b\r\n\r\n) if not header.strip(): continue name_match re.search(rfilename(.*?), header.decode(utf-8, ignore)) if not name_match: continue filename name_match.group(1) filename os.path.basename(filename) if not filename: continue if content.endswith(b\r\n): content content[:-2] if content.startswith(b\r\n): content content[2:] save_path os.path.join(UPLOAD_DIR, filename) i 1 base filename while os.path.exists(save_path): name, ext os.path.splitext(base) save_path os.path.join(UPLOAD_DIR, f{name}_{i}{ext}) i 1 with open(save_path, wb) as f: f.write(content) saved.append(os.path.basename(save_path)) self.send_response(200) self.send_header(Content-Type, text/html; charsetutf-8) self.end_headers() msg h2上传完成/h2ul for name in saved: msg fli{name}/li msg /ulpa href/继续上传/a/p self.wfile.write(msg.encode(utf-8)) if __name__ __main__: server ThreadingHTTPServer((0.0.0.0, 8000), UploadHandler) print(Upload server running at http://0.0.0.0:8000) print(局域网内访问 http://你的IP:8000) server.serve_forever()保存为upload_server.py运行python upload_server.py浏览器打开http://localhost:8000选择一个文件提交就能看见文件被保存到uploads目录。手机上访问http://192.168.x.x:8000效果一样。这段代码有几个点值得说明使用ThreadingHTTPServer而不是单线程的HTTPServer避免一个手机在上传大文件时其他设备连不进来。文件名冲突自动加_1、_2后缀避免覆盖已有文件。用os.path.basename去掉路径防止刻意构造的../../路径写回上层目录增加一点安全性。上传界面非常简单但功能完整中文文件名也能处理因为代码全程操作字节只有从头部解析文件名时用 UTF-8 解码。4.3 为什么不推荐用cgi.FieldStorage解析可能有读者看过老教程里用cgi模块解析上传。我只能说那个模块在 Python 3.13 里已经被移除了新写的代码最好不要再依赖它。就算你现在用的 Python 3.10 还能跑等到升级环境就会突然崩。上面这段自己切 boundary 的写法虽然原始但不依赖任何被废弃的模块从“长期能跑”的角度看反而更可靠。如果你嫌这段代码处理不了多个文件没关系浏览器多选文件时不同文件基本上会形成独立的 section按现在的逻辑循环存盘即可。单个大文件的内存占用是个问题因为是整体读入body再拆分的传 2GB 文件会直接内存爆炸。极简工具定位在 1GB 以下的文件足够应付大部分照片、视频和文档。真要传超大文件我建议回到http.server下载模式或者换rsync。4.4 实测感受手机相册传到电脑我自己测试过把手机里的几百张照片传到电脑。步骤是手机连 WiFi浏览器输入电脑 IP:8000打开上传页在相册里选一批照片一次提交。在 802.11ac 路由器下传 500MB 照片大概不到一分钟。速度不是瓶颈手机浏览器在选照片多时反而会卡一下。如果照片数量很多建议分批选或者把相册压缩成一个 zip 再传。这个工具也可以用在公司内部收文件同事把各自的项目资料压缩包丢进来一台电脑集中收集省去用聊天工具反复传输。不过要注意这只是一个临时方案长期使用还需要加用户认证、日志和磁盘限额。5. 加餐用 Flask 写一个局域网内可访问的小 API5.1 什么时候开始需要 Flaskhttp.server擅长处理文件型需求但它不适合做 JSON API。比如你想让手机端获取电脑上某个程序的状态最好返回一段 JSON这时候写一个原生 Handler 也能实现但麻烦。Flask 的装饰器路由把 URL 和函数绑定得非常直观开发效率高很多。极简入门并不排斥 Flask只是多一步安装pip install flask如果你前面建了虚拟环境记得先激活再安装。5.2 最简 Flask 应用局域网联调的关键写法from flask import Flask, jsonify app Flask(__name__) app.route(/api/time) def get_time(): import datetime return jsonify({time: datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S)}) if __name__ __main__: app.run(host0.0.0.0, port5000)保存成api_server.py运行后会监听所有网卡。手机或另一台电脑访问http://192.168.x.x:5000/api/time就能看到当前时间。这个场景常用来做前后端联调前端开发者在电脑的浏览器里访问同一个 API手机 App 访问另一个 IP但用的是同一个后端。这里最关键的是一行代码host0.0.0.0。很多新手写app.run()结果只能本机访问因为 Flask 默认只监听127.0.0.1局域网设备连不上。0.0.0.0表示绑定所有网络接口这样电脑的局域网 IP 上才能看到服务。5.3 Flask 代码热重载的坑以及临时安全建议Flask 的debugTrue会开启代码热重载改文件自动重启很方便。但 debug 模式在局域网内有一个风险Werkzeug 的调试器是交互式的别人如果拿到地址可能执行任意 Python 代码。我不建议对局域网开放 debug 的调试器。极简联调时可以先用debugFalse改代码后手动重启服务真要打开 debug也尽量只在本机127.0.0.1上访问。另外Flask 开发服务器不能用来扛并发这本来就是给开发调试用的。如果有几十个人同时访问一个接口请换 Gunicorn 或 Waitress但那已经超出“极简入门”的范畴了。写到这里其实你已经有能力判断同一个需求用http.server还是 Flask取决于有没有动态参数和 JSON 返回。6. 这些坑我基本都踩过整理一份规避清单6.1 端口占用不是换一个端口就没事之前说过端口占用但这里有个细节换端口之后手机端访问的地址也要同步换而且第一次占用的进程可能还会被系统自动拉起来。比如我用 Visual Studio Code 调试过 Node 项目它把 9229 端口占着你换 3000下次又占 3000。所以要找到占用源Windowsnetstat -ano | findstr :8000 tasklist | findstr 1234macOS/Linuxlsof -i :8000如果是你自己启动的另一个服务关掉即可如果不是你需要保留的服务kill对应进程也别心疼。极简场景里我建议固定一个端口比如 8000每次启动前如果被占用再检查不要反复换否则手机收藏夹里的地址就全乱了。6.2 中文文件名和编码问题我在 Windows 上遇到过一个非常典型的问题文件名是中文上传到服务器后变成乱码。原因有两个层面浏览器在 HTTP 头部里的filename通常会做 URL 编码也可能直接使用 UTF-8 字节Windows 控制台默认编码是 GBK打印日志时看到乱码不一定代表文件保存失败。处理办法是不要在终端里肉眼判断直接打开uploads目录确认文件。代码里可以加一句raw_name name_match.group(1) filename urllib.parse.unquote(raw_name, encodingutf-8)这能让某些客户端传上来的文件名还原成正常中文。另一个朴素建议在同一个局域网内传文件尽量让文件名保持纯字母或数字英文能省掉 90% 的编码烦恼。这不是技术向后退而是极简工具要控制变量。6.3 局域网服务器的安全边界再说一遍这篇里所有服务都只听局域网别轻易映射到公网。原因很简单服务端代码没有做用户认证局域网内任何人都能上传、下载。没有 HTTPS传输内容在局域网内裸奔虽然比公网稍好但也不适合敏感文件。如果电脑上跑着其他服务暴露到公网等于把自己的局域网大门开了一条缝。我的态度是临时用、自己人用可以要长期开放给外部用户直接上正规 Web 服务器设置身份验证和访问日志。不在run()或http.server外面做端口映射。这不是为难自己是保护数据。6.4 怎么让它不占住你的终端后台运行的简单姿势有时候想一直开着服务又不想让终端一直霸屏。Linux/macOS 可以nohup python -m http.server 8000 server.log 21 Windows 下可以写一个.bat用start /b python -m http.server 8000后台跑或者用pythonw.exe。但后台服务有一个现实问题没有终端日志找不到报错。我的做法是后台运行时把输出重定向到文件出错时查日志。这比用各种进程守护工具简单得多也符合“极简”的定位。如果你发现某个服务需要每天自动启动那说明它已经从临时工具变成长期系统了这时候更推荐用 Nginx、Docker 或系统服务而不是在 Python server 上硬扛。这不算打脸是对工具边界的合理判断。