惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
有赞技术团队
有赞技术团队
www.infosecurity-magazine.com
www.infosecurity-magazine.com
大猫的无限游戏
大猫的无限游戏
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threatpost
V
Visual Studio Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - Franky
人人都是产品经理
人人都是产品经理
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The Cloudflare Blog
N
News and Events Feed by Topic
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
AWS News Blog
AWS News Blog
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Spread Privacy
Spread Privacy
J
Java Code Geeks
博客园 - 聂微东
T
Tor Project blog
宝玉的分享
宝玉的分享
博客园 - 叶小钗
Webroot Blog
Webroot Blog
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
H
Heimdal Security Blog
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
MongoDB | Blog
MongoDB | Blog
I
InfoQ
Security Latest
Security Latest
Martin Fowler
Martin Fowler
Hacker News: Ask HN
Hacker News: Ask HN
P
Privacy International News Feed
C
CERT Recently Published Vulnerability Notes
Latest news
Latest news
雷峰网
雷峰网
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cisco Blogs
H
Help Net Security
L
LINUX DO - 最新话题
L
LINUX DO - 热门话题

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) 聊聊 fetch 使用中我踩过的那些坑和正确打开方式 FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 用了loguru我才明白,Python日志还能这么写 FastAPI 后台任务:BackgroundTasks 的使用场景与注意事项 FastAPI配置管理避坑指南:从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚 FastAPI 文件上传避坑全指南:分块存盘、类型校验与安全兜底 FastAPI + Pydantic 模型终极实战手册:从能跑就行到固若金汤,这些技巧你一定用得上 FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透 FastAPI订单防超卖实战:从数据库锁到Saga分布式事务,这一篇给你理清了 FastAPI 生产环境避坑指南:用 Alembic 管理数据库迁移,别再手动改表结构了! FastAPI服务半夜又挂了?先别急着重启,查查你的数据库连接池“池子”是不是漏了 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里 我用fastapi-scaff搭了个项目,两天工期缩到两小时,老板以为我开挂了 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! FastAPI自动生成的API文档太丑?我花了一晚上把它改成了客户愿意付费的样子 告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战 FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南” FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案 FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了! FastAPI + Celery 实战:异步任务的坑与解法,我帮你踩了一遍 FastAPI子应用挂载:别再让root_path坑你一夜 FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞! FastAPI单元测试实战:别等上线被喷才后悔,TestClient用对了真香! FastAPI状态共享秘籍:别再让中间件、依赖和路由“各自为政”了! FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了! 从0到1,FastAPI + PostgreSQL + Tortoise ORM 实战避坑指南 FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀 FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
别再数据线了!用FastAPI 5分钟搭个局域网文件+剪贴板神器
一名程序媛呀 · 2026-03-26 · via 博客园 - 一名程序媛呀

你是不是也经历过这样的瞬间?

明明手机就在手边,想传个截图到电脑修图,结果打开微信,点开“文件传输助手”,发送,等半天,还得在电脑上登录微信,下载……一套流程走下来,修图的心情都没了。反过来,想把电脑上写好的文案发给手机,更麻烦。

至于剪贴板?手机看到的好句子,想在电脑上搜一下,要么靠手打,要么靠发条消息再复制。这哪是科技时代,这简直是“手动挡”生活嘛!

作为每天在电脑和手机之间来回切换的一名程序媛,这个问题困扰我很久了。市面上的“隔空传送”不是不好用,是生态限制太死。终于有一天,我忍不了了,决定自己动手,用咱程序员最熟悉的FastAPI,搭一个“私家传送站”。

核心摘要: 今天这篇文,不是让你读文档。我会手把手带你写一个轻量级的Web应用,跑在你电脑上。然后,只要是连在了同一局域网的手机或平板电脑等网络设备,打开浏览器,就能上传下载文件,还能同步剪贴板。全程代码不超过100行,安全、私有、还免费!

📦 先看看咱要搭的东西长啥样

想象一下,你电脑上开了个服务,手机浏览器里打开一个页面。页面上半部分是一个文件上传区,点一下,选手机里的照片,秒传回电脑指定文件夹。页面下半部分是一个剪贴板文本框,你在电脑上复制了代码片段,打开手机页面,它就在那等着你粘贴。反之亦然。

简单,直接,没有中间商赚差价。

🎯 为什么是FastAPI?

你可能会问:为啥不选Flask或者Django?好问题!我选FastAPI的原因很简单:。这里的“快”是双关,一是它性能好,底层是异步的;二是它开发极快,自带交互式API文档,调试起来爽歪歪。对于咱们这种小工具,简直量身定做。

而且,它的文件上传处理,是我用过最优雅的,没有之一。

⚙️ 实战:从零开始的私家传送站

好,咱们不废话,直接撸代码。我会把完整代码拆开讲,你复制粘贴就能跑。不过,先别急着复制,听我说个坑:Python版本建议3.8以上,否则有些依赖会让你怀疑人生。当初我偷懒用3.7,结果一个依赖报错,排查了一小时,血泪教训!

📁 第一步:创建项目文件夹,安装依赖

打开终端,敲几行命令:

uv init file_clipboard_server # 创建虚拟环境
cd file_clipboard_server # 进入项目目录
uv add fastapi uvicorn python-multipart # 安装依赖

这里重点来了!python-multipart 这个库是必须的,没有它,FastAPI没法解析上传的文件。官方文档虽然说了,但很多人看文档不仔细,漏掉这步,然后回来问我为啥上传不了。记住了啊!

💻 第二步:编写核心代码 main.py

在项目文件夹里新建一个 main.py 文件,把下面这段代码丢进去。我加了详细注释,你就当我是边写边跟你唠嗑。

from fastapi import FastAPI, File, UploadFile, Request
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
import os
import uvicorn

app = FastAPI()

# 用来存放上传文件的目录,没有就自动创建
UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)

# 用来存剪贴板内容的简单变量(注意:重启服务就没了,但够用了)
clipboard_content = ""

# 主页,返回一个简单的HTML页面
@app.get("/", response_class=HTMLResponse)
async def main_page():
    # 这个HTML我稍后会解释,你先复制
    html_content = """
<!DOCTYPE html>
<html>
<head>
    <title>私家传送站</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        body { font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; max-width: 600px; margin: 40px auto; padding: 0 20px; background: #f9f9f9; }
        .card { background: white; border-radius: 16px; padding: 24px; margin-bottom: 24px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
        h2 { margin-top: 0; font-size: 1.5rem; }
        input, textarea, button { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ddd; border-radius: 8px; font-size: 16px; box-sizing: border-box; }
        button { background-color: #3498db; color: white; border: none; font-weight: bold; cursor: pointer; }
        button:hover { background-color: #2980b9; }
        .result { margin-top: 16px; padding: 12px; background: #f0f7ff; border-radius: 8px; font-size: 14px; word-break: break-all; }
    </style>
</head>
<body>
    <div class="card">
        <h2>📎 文件互传</h2>
        <form id="uploadForm" enctype="multipart/form-data">
            <input type="file" name="file" id="fileInput" required>
            <button type="submit">上传到电脑</button>
        </form>
        <div id="uploadResult" class="result"></div>
    </div>

    <div class="card">
        <h2>📋 剪贴板同步</h2>
        <textarea id="clipText" rows="4" placeholder="在这里粘贴或查看文本..."></textarea>
        <button id="syncToServer">📤 同步到电脑</button>
        <button id="loadFromServer">📥 从电脑获取</button>
        <div id="clipResult" class="result"></div>
    </div>

    <script>
        // 文件上传逻辑
        document.getElementById('uploadForm').onsubmit = async (e) => {
            e.preventDefault();
            const file = document.getElementById('fileInput').files[0];
            if (!file) return;
            const formData = new FormData();
            formData.append('file', file);
            const res = await fetch('/upload', { method: 'POST', body: formData });
            const data = await res.json();
            document.getElementById('uploadResult').innerHTML = `✅ ${data.filename} 上传成功`;
        };

        // 剪贴板同步
        document.getElementById('syncToServer').onclick = async () => {
            const text = document.getElementById('clipText').value;
            const res = await fetch('/clipboard', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: text }) });
            const data = await res.json();
            document.getElementById('clipResult').innerHTML = `📤 ${data.message}`;
        };

        document.getElementById('loadFromServer').onclick = async () => {
            const res = await fetch('/clipboard');
            const data = await res.json();
            document.getElementById('clipText').value = data.content;
            document.getElementById('clipResult').innerHTML = `📥 已同步: ${data.content.substring(0, 50)}`;
        };
    </script>
</body>
</html>
"""
    return html_content

# 文件上传接口
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
    file_path = os.path.join(UPLOAD_DIR, file.filename)
    # 防止文件名冲突的小处理,这里简单覆盖同名文件
    with open(file_path, "wb") as buffer:
        buffer.write(await file.read())
        return {"filename": file.filename, "message": "上传成功"}

# 获取剪贴板内容
@app.get("/clipboard")
async def get_clipboard():
    return {"content": clipboard_content}

# 更新剪贴板内容
@app.post("/clipboard")
async def update_clipboard(request: Request):
    global clipboard_content
    data = await request.json()
    clipboard_content = data.get("content", "")
    return {"message": "剪贴板已更新"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

这段代码里,HTML部分我故意写得比较“原始”,就是为了让大家看得懂。别嫌丑,功能第一!

🚀 第三步:跑起来!

在终端运行:

uv run main.py

看到 Uvicorn running on http://0.0.0.0:8000 这样的提示,就说明成功了!

现在,拿起你的手机,连上和电脑同一个Wi-Fi,打开浏览器,输入 电脑的局域网IP:8000。怎么看电脑IP?Windows用 ipconfig,Mac/Linux用 ifconfig,找到类似 192.168.x.x 的地址就行。

是不是以为这样就完了?别急,这里有个容易翻车的点:防火墙。如果手机死活打不开,八成是电脑防火墙拦住了8000端口。去防火墙设置里加一条入站规则,允许8000端口访问。我当时就卡在这步,折腾了好久。

🎉 成品长这样

当手机页面打开的那一刻,你会看到一个干净的两块区域。点“上传到电脑”,你手机里的图片或文件就嗖的一下飞到电脑的 uploads 文件夹里了。

在电脑上复制粘贴一段文字到文本框中,点击“同步到电脑”,在手机上点“从电脑获取”,它立刻就出现了。反过来,在手机上输入文字,点“同步到电脑”,这个文字就被存到了服务端的变量里,你在电脑上随时可以调用。

这个功能纯属顺手一加,结果成了真香现场。我经常在手机上刷到好文章,复制金句,点一下同步,电脑上打开IDE写文章时,直接就能贴上去。无缝衔接!

⚠️ 几点不得不提的注意事项

- 安全性:这个小工具只建议在内网(家里/公司Wi-Fi)使用。如果暴露在公网,没有做任何鉴权,别人也能访问,风险很大。别图方便把端口映射出去!

- 剪贴板持久化:我们这里用了内存变量,服务重启就没了。如果想持久化,可以改成存文件或数据库,代码改动很小,留作你的课后作业吧。

- 大文件上传:如果传视频这种大文件,可以加上进度条,或者用分片上传。但作为日常传点照片文档,这个版本绰绰有余。

🚧 进阶思考:还能怎么玩?

当你把这个小东西跑起来之后,你会发现它的潜力远不止这些。你可以把它改成一个临时的“公共相册”,朋友们聚会时扫码上传照片;或者把它变成一个跨平台的“写作同步工具”,在手机上写大纲,电脑上直接继续。甚至,你可以用类似的方法,实现电脑控制手机播放PPT?脑洞大开的时刻来了!

这个工具的选择,好比选螺丝刀,不是最贵的就好,而是顺手、能解决问题的就是最好的。FastAPI这个小螺丝刀,我用得挺顺手,希望你也是。

好啦,以上就是今天的全部内容。

如果你也跟着跑起来了,恭喜你,又多了一个专属的效率工具!如果卡在某个环节,欢迎留言,我会第一时间帮你看看,毕竟这些坑我也都踩过。

❤️ 如果这篇文章帮到了你,点赞、收藏、关注 支持一下~ 你的支持是我继续分享“踩坑笔记”的最大动力!

咱们下篇再见,继续聊聊那些让生活更简单的技术小玩意儿。