news 2026/10/30 13:11:02

微信小程序上传图片视频到Flask后台:可运行Demo与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序上传图片视频到Flask后台:可运行Demo与避坑指南

简介:这份资源面向微信小程序开发者与Flask后端初学者,提供图片、视频上传至服务器存储的完整前后端示例,可直接运行调试,适合作为课程作业、功能原型或项目练手素材。压缩包共12个文件,约5KB,包含5个json配置、3个js逻辑脚本、2个wxss样式、1个wxml页面结构及1个py后端文件,前端小程序与后端上传接口一一对应,目录按前后端分置,便于快速定位与二次修改。已有421人学习下载,说明该示例在同类需求中具备一定参考价值。读者可从中获得小程序端选择媒体、发起上传请求的完整流程,以及Flask端接收文件并落盘存储的实现思路,配合配套博文教程可进一步理解参数传递与接口联调细节,遇到问题也可与作者交流讨论,适合希望打通小程序与后端文件上传链路的开发者参考。

1. 微信小程序上传图片视频到后台存储:一份能直接跑通的 Flask 落地 Demo

小程序端选完图、点上传,后台却只收到一个空文件或者 413,这种场景我见过太多次。这份 Demo 干的事很具体:把微信小程序里wx.chooseMedia选出来的图片和视频,通过wx.uploadFile传到 Flask 后台,落盘存储并返回可访问路径。它解决的是「前端选得了、后端收得住、文件找得回」这条最短链路,适合刚接手小程序文件上传需求的开发者、要做课程设计的学生,以及想给现有 Flask 项目补上传能力的后端同学。整套东西不依赖对象存储,本地磁盘就能跑,先把链路打通再谈扩展,这是我的一贯顺序。

2. 拆开这份 Demo:Flask 接收端与小程序上传端的职责边界

拿到一个上传 Demo,先别急着跑,得看清两端各自管什么。很多翻车不是代码错,是职责没分清——前端以为后端会压缩,后端以为前端已经限制了大小,结果两边都没做,一个 200MB 的视频直接把服务打挂。这一章把 Demo 的结构拆开讲,让你知道每个文件该放哪、每段逻辑为什么这么写。

2.1 项目结构与依赖清单

这份 Demo 的目录结构不复杂,但每个位置都有讲究。我一般会按下面这样组织,前后端分开,上传目录独立出来,方便后面挂载和清理:

upload-demo/ ├── server/ │ ├── app.py # Flask 主入口 │ ├── config.py # 上传大小、允许类型等配置 │ ├── uploads/ # 落盘目录(运行时生成) │ └── requirements.txt └── miniprogram/ ├── pages/upload/ │ ├── upload.js # 选图 + 上传逻辑 │ ├── upload.wxml │ └── upload.wxss └── app.json

依赖就三个,别装一堆用不上的:

# server/requirements.txt Flask==3.0.3 Flask-Cors==4.0.1 Werkzeug==3.0.3

Flask是主框架,Flask-Cors处理小程序开发阶段的跨域预检,Werkzeug里的secure_filename用来清洗文件名。这里有个血泪经验:secure_filename会把中文文件名几乎全部剥掉,如果你的业务需要保留原始中文名,得自己写一套清洗逻辑,不能直接用它返回的结果当最终文件名,否则用户上传「产品图.jpg」最后变成「.jpg」,排查起来很费劲。

2.2 Flask 接收端:从 request.files 到安全落盘

后端接收的核心是request.files,但真正决定稳不稳的是落盘前的校验。下面这段是我在这个 Demo 里用的接收逻辑,能直接抄:

# server/app.py import os import uuid from flask import Flask, request, jsonify from flask_cors import CORS from werkzeug.utils import secure_filename app = Flask(__name__) CORS(app) # 配置区:这几个值建议放 config.py,别硬编码在路由里 UPLOAD_DIR = os.path.join(os.path.dirname(__file__), "uploads") ALLOWED_EXT = {"jpg", "jpeg", "png", "gif", "mp4", "mov"} MAX_SIZE = 50 * 1024 * 1024 # 单文件 50MB 上限 os.makedirs(UPLOAD_DIR, exist_ok=True) app.config["MAX_CONTENT_LENGTH"] = MAX_SIZE def allowed_file(filename: str) -> bool: # 只认扩展名,且必须带点,防止无扩展名文件混入 return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXT @app.route("/api/upload", methods=["POST"]) def upload(): # 1. 字段名必须和小程序端 wx.uploadFile 的 name 一致 if "file" not in request.files: return jsonify(code=400, msg="缺少 file 字段"), 400 f = request.files["file"] if f.filename == "": return jsonify(code=400, msg="文件名为空"), 400 if not allowed_file(f.filename): return jsonify(code=400, msg="不支持的文件类型"), 400 # 2. 用 uuid 重命名,避免同名覆盖和路径穿越 ext = f.filename.rsplit(".", 1)[1].lower() safe_name = f"{uuid.uuid4().hex}.{ext}" save_path = os.path.join(UPLOAD_DIR, safe_name) # 3. 分块写入,大文件不至于一次性吃满内存 with open(save_path, "wb") as out: while chunk := f.stream.read(1024 * 1024): out.write(chunk) # 4. 返回相对路径,前端拼域名即可访问 return jsonify(code=0, msg="ok", data={"path": f"/uploads/{safe_name}"}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)

逐段说下为什么这么写。第一,字段名file是硬约定,小程序端wx.uploadFile的name参数写什么,这里就必须是什么,两边对不上就是「缺少 file 字段」,这是最高频的报错。第二,用uuid4().hex重命名而不是保留原名,一是防同名覆盖,二是防../这类路径穿越,secure_filename虽然也能防,但会吃掉中文,所以我干脆自己生成。第三,f.stream.read(1024 * 1024)分块写,1MB 一块,50MB 的文件也就循环 50 次,内存占用可控;如果直接f.save(),小文件没问题,大视频在并发下容易把内存顶上去。第四,MAX_CONTENT_LENGTH是 Flask 层面的硬闸,超过直接返回 413,比在业务代码里判断更早生效。

参数上,MAX_SIZE设 50MB 是个折中值,微信小程序wx.uploadFile单文件本身有上限,视频稍微大一点就传不动,实际项目里视频建议走分片或者直传对象存储,这个 Demo 先覆盖图片和短视频场景。ALLOWED_EXT里我加了mov,因为 iOS 拍出来的视频常见这个格式,不加的话苹果用户一传就报「不支持的类型」。

2.3 小程序端:wx.chooseMedia 与 wx.uploadFile 的配合

前端这块,选媒体和上传是两个 API,中间要处理好临时路径。核心代码如下:

// miniprogram/pages/upload/upload.js Page({ data: { filePath: "", mediaType: "image", uploading: false, }, // 选图或选视频,mediaType 决定选哪种 chooseMedia() { const that = this; wx.chooseMedia({ count: 1, mediaType: [this.data.mediaType], // image / video / mix sourceType: ["album", "camera"], maxDuration: 30, // 视频最长 30 秒,和后台大小限制呼应 success(res) { const file = res.tempFiles[0]; that.setData({ filePath: file.tempFilePath, mediaType: file.fileType === "video" ? "video" : "image", }); }, fail(err) { console.error("选择失败", err); }, }); }, // 上传到 Flask 后台 doUpload() { if (!this.data.filePath) { wx.showToast({ title: "请先选择文件", icon: "none" }); return; } if (this.data.uploading) return; // 防重复点击 this.setData({ uploading: true }); const that = this; wx.uploadFile({ url: "http://127.0.0.1:5000/api/upload", // 真机调试换成局域网 IP filePath: this.data.filePath, name: "file", // 必须和后台 request.files 的键一致 formData: { source: "miniprogram" }, // 附加字段,后台可选用 timeout: 60000, success(res) { // 注意:res.data 是字符串,要手动 JSON.parse const body = JSON.parse(res.data); if (body.code === 0) { wx.showToast({ title: "上传成功" }); console.log("访问路径", body.data.path); } else { wx.showToast({ title: body.msg || "上传失败", icon: "none" }); } }, fail(err) { console.error("上传失败", err); wx.showToast({ title: "网络异常", icon: "none" }); }, complete() { that.setData({ uploading: false }); }, }); }, });

几个关键点。wx.chooseMedia的mediaType传数组,["image"]只选图,["video"]只选视频,["mix"]混选,Demo 里用data.mediaType控制,方便切换。maxDuration: 30是视频时长上限,单位秒,和后台 50MB 的大小限制配合,避免用户选了个 5 分钟的视频传半天失败。wx.uploadFile的name必须和后台request.files["file"]对上,这是硬约束。还有一个新手常踩的点:success回调里的res.data是字符串不是对象,直接res.data.path会拿到undefined,必须JSON.parse一次,这个坑我在早期项目里栽过,排查了半天以为是后台没返回。

url里的127.0.0.1只在开发者工具里有效,真机调试必须换成电脑的局域网 IP,比如http://192.168.x.x:5000/api/upload,而且手机和电脑要在同一个网络下。这一步不做,真机上永远是「网络异常」。

3. 跑通全链路:从本地启动到真机验证的完整步骤

代码看懂了不代表能跑起来,上传这条链路涉及端口、网络、权限、格式四道关,任何一道没过都是失败。这一章按启动顺序走一遍,每步给出验证方法,让你知道卡在哪一环。

3.1 启动 Flask 并验证接口可用

先把后端跑起来,别急着开小程序。启动命令:

cd server pip install -r requirements.txt python app.py

看到Running on http://0.0.0.0:5000就说明起来了。但「起来了」不等于「能用」,我习惯先用 curl 打一发,确认接口本身没问题:

# 用一个本地图片测试,-F 的 file 对应后台字段名 curl -X POST http://127.0.0.1:5000/api/upload \ -F "file=@./test.jpg" \ -v

正常返回应该是{"code":0,"msg":"ok","data":{"path":"/uploads/xxxx.jpg"}}。如果返回 413,说明文件超了MAX_CONTENT_LENGTH;返回 400 且提示「不支持的文件类型」,检查扩展名是否在ALLOWED_EXT里;返回 400 提示「缺少 file 字段」,说明-F的键名写错了。这一步用 curl 先过,能把「后端自身的问题」和「前后端联调的问题」隔离开,省得两头猜。

还要注意uploads目录的写权限。Linux 下如果 Flask 以低权限用户跑,目录属主不对,open(save_path, "wb")会直接抛PermissionError,日志里能看到,但小程序端只会显示「上传失败」,容易误判成网络问题。启动前ls -ld uploads看一眼属主,不对就chown一下。

3.2 开发者工具里的域名与跨域配置

微信开发者工具默认会校验合法域名,本地127.0.0.1不在白名单里,直接请求会被拦。开发阶段的做法是:在开发者工具右上角「详情 → 本地设置」里勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」。这一步勾上,本地 HTTP 请求才能发出去。

跨域这块,Flask 端已经用CORS(app)全局放开了,开发阶段够用。但要注意,CORS(app)是允许所有来源,上线前必须收紧到具体域名,否则等于把接口裸奔在公网。我一般会在config.py里留一个ALLOWED_ORIGINS列表,开发时是["*"],上线前改成小程序后台配置的合法域名。

还有一个容易忽略的点:wx.uploadFile走的是multipart/form-data,不是普通POST,所以后台不能用request.get_json()去取参数,附加字段要用request.form.get("source")。这个区别在联调时经常让人困惑——明明前端formData传了值,后台get_json()却是None。

3.3 真机调试:局域网 IP、HTTPS 与大小限制

开发者工具跑通后,真机是另一道坎。三个必须处理的点:

第一,url换成局域网 IP。电脑上ipconfig(Windows)或ifconfig(macOS/Linux)查到本机 IP,比如192.168.1.100,小程序里改成http://192.168.1.100:5000/api/upload。手机连同一个 WiFi,浏览器先访问http://192.168.1.100:5000看能不能通,通了再传。

第二,真机默认要求 HTTPS。开发阶段可以在小程序后台「开发管理 → 开发设置 → 服务器域名」里配置,但 HTTP 的 IP 加不进去。常规做法是真机调试时打开「调试模式」,或者在开发者工具里用「真机调试」功能,它会临时放宽限制。正式环境必须上 HTTPS,这是硬要求,Demo 阶段先用调试模式绕过。

第三,大小限制要对齐。微信小程序wx.uploadFile单文件有上限,视频稍大就传不动,后台的 50MB 和前端maxDuration: 30要一起调。我见过前端不限时长、后台限 50MB 的配置,用户选个 2 分钟视频,传到 80% 失败,体验极差。两边阈值必须一致,或者前端选完就先判断tempFiles[0].size,超了直接提示,别等传一半才报错。

验证顺序建议:curl 通 → 开发者工具通 → 真机同 WiFi 通 → 真机调试模式通。每步都过了,这条链路才算真正跑通。

4. 避坑与排查:上传链路上最容易翻车的五个点

上传这活儿,代码量不大,坑却不少,而且大多表现为「上传失败」四个字,信息量极低。这一章按「现象 → 原因 → 解决」把高频问题列清楚,照着对,能省下大量瞎猜的时间。

4.1 现象:后台收到空文件或文件名丢失

原因通常是前端name和后台字段名不一致,或者用了secure_filename处理中文名。前者表现为request.files里根本没有对应键,后者表现为落盘文件名变成.jpg这种残缺形态。解决:先确认wx.uploadFile的name和request.files["file"]完全一致;中文名场景不要直接用secure_filename的返回值,改成「uuid + 原扩展名」,原始名单独存数据库字段。

4.2 现象:413 Request Entity Too Large

原因就是文件超了MAX_CONTENT_LENGTH。但要注意,这个限制是 Flask 层面的,Nginx 反代时还有一层client_max_body_size,两层都要调。解决:Flask 里设app.config["MAX_CONTENT_LENGTH"],Nginx 里设client_max_body_size 50m;,两边数值对齐。只调一边,另一边照样拦。

4.3 现象:真机上一直「网络异常」

原因多半是url还是127.0.0.1,或者手机和电脑不在同一网段。解决:换成电脑局域网 IP,手机浏览器先访问确认连通;再检查电脑防火墙是否放行了 5000 端口,Windows 防火墙默认会拦入站连接,这个特别隐蔽。

4.4 现象:iOS 上传视频报「不支持的类型」

原因是 iOS 拍的视频常见.mov格式,ALLOWED_EXT里没加。解决:把mov加进白名单,或者前端选完后判断fileType,非白名单格式直接提示用户。别指望用户自己转格式,体验上过不去。

4.5 现象:并发上传时部分请求失败或文件损坏

原因是多个请求写同名文件,或者磁盘 IO 打满。用 uuid 重命名能解决同名覆盖,但磁盘 IO 这块要靠限流。解决:前端加uploading状态防重复点击(Demo 里已有),后端如果并发高,考虑加队列或者直接上对象存储,本地磁盘不适合高并发写入场景。

5. 从 Demo 到可用:三个让上传链路更稳的改造技巧

Demo 跑通只是起点,真要用起来,还得补几块。这一章说三个我每次都会加的改造,都是踩过坑之后形成的习惯。

第一个是文件类型二次校验。光看扩展名不够,用户把.exe改成.jpg照样能过。稳妥做法是读文件头几个字节判断真实类型,图片看 magic number(JPEG 是FF D8 FF,PNG 是89 50 4E 47),视频看ftyp标识。下面这段是图片校验的简化版:

def sniff_image_type(path: str) -> str | None: # 读前 8 字节判断真实图片类型,防止改扩展名绕过 with open(path, "rb") as f: head = f.read(8) if head[:3] == b"\xff\xd8\xff": return "jpeg" if head[:8] == b"\x89PNG\r\n\x1a\n": return "png" if head[:6] in (b"GIF87a", b"GIF89a"): return "gif" return None

落盘后调一次,返回None就删文件并报错。这一步能挡掉大部分伪装文件,成本很低。

第二个是上传进度反馈。wx.uploadFile返回的是UploadTask对象,可以监听onProgressUpdate,把百分比推到页面,用户不至于盯着转圈干等。视频文件尤其需要,30 秒的视频在弱网下能传一分多钟,没进度条用户早退了。

第三个是清理策略。uploads目录只增不减,跑几个月就撑爆磁盘。我一般加个定时任务,按修改时间清理超过 N 天的文件,或者落库时记录路径和创建时间,定期对账删除孤儿文件。这个不做,迟早有一天磁盘满了,上传全线挂掉,那时候再补就晚了。

改造项解决什么问题实现成本
文件头校验改扩展名绕过类型限制低,一个函数
进度反馈大文件上传无感知低,监听回调
定时清理磁盘只增不减中,需定时任务

这三个改造里,文件头校验是我最坚持的一个。早些年有个项目没做,用户传了个改名的脚本上来,虽然没造成实际影响,但事后复盘吓出一身冷汗。从那以后我每次做上传功能,落盘后的类型校验都强制走一遍,不管前端有没有限制——前端的限制永远只是体验优化,真正的安全边界必须在后端。希望这套东西能帮到你,少走几个我走过的弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 12:58:10

pdf2doc.exe在Win10/Win11的安装配置与闪退排查指南

简介:面向需要在 Windows 上把 PDF 批量转成 Word 的办公人群,这是一份基于 Python 自行整合打包的转换工具,解压后直接运行 pdf2doc.exe 即可使用,免安装、无广告,支持单个文件、多文件以及整个文件夹的批量转换。压缩…

作者头像 李华
网站建设 2026/10/30 12:57:38

C# Modbus TCP读写汇川PLC寄存器实战指南

简介:本资源是一套基于C#实现Modbus TCP协议与汇川PLC通信的完整开发示例,面向工业自动化领域的.NET开发者、PLC上位机工程师及工控系统集成学习者,解决现场设备数据采集、远程监控与协议调试等实际问题。压缩包共148个文件,涵盖9…

作者头像 李华
网站建设 2026/10/30 12:39:01

液氢、液氦这类高端特气项目,国产仪表能替代进口吗?

这些年跑航天、超导磁体、核聚变、前沿科研的极低温项目,被问得最多的一个问题就是:液氢、液氦场景里的测控仪表,国产能不能替代进口?站在一线做低温测控的角度,我的看法很直接:不是简单的 “能” 或者 “不…

作者头像 李华
网站建设 2026/10/30 12:27:10

网络质量监控选型:CentOS 7安装Smokeping全记录

简介:这是一份面向网络运维工程师、系统管理员及开发者的 CentOS 7 下 Smokeping 快速部署手册。资源以 yum 安装方式为主线,先说明需要启用 EPEL 源,再依次讲解 /etc/smokeping/config 中监控主机、探测器与图表等配置,以及通过 …

作者头像 李华
网站建设 2026/10/30 12:17:10

如何使用Pion WebRTC实现实时连接状态监控和数据统计

如何使用Pion WebRTC实现实时连接状态监控和数据统计 WebRTC实时通信技术在现代应用中发挥着关键作用,而Pion WebRTC数据统计和监控功能让开发者能够深入了解连接质量、性能指标和网络状况。无论你是构建视频会议系统、在线教育平台还是实时游戏应用,掌…

作者头像 李华
网站建设 2026/10/30 12:05:00

效率工具软件选型与自动化指南:从工具收集到流程自动化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华