uniapp 文件上传实战:uni.uploadFile 选型、封装与跨端排障全指南
2026/9/9 12:03:09 网站建设 项目流程

做 uniapp 这两年,我大概写过不下二十种文件上传的流程:从最开始的图片上传、头像更换,到后来的视频、音频、Excel 表格导入,再到带进度条的多文件批量上传。每次项目一换,上传这套逻辑就得重写一版,踩过的坑能列一长串。如果你现在正准备在 uniapp 项目里做 APP 端的文件上传,或者已经写完一版但总觉得不稳定,这篇内容就是给你准备的。我不会去贴官方文档,而是把 uni.uploadFile 这套东西从选型、参数、封装到排障完整过一遍,讲清楚每个决策背后的原因,把我实际项目中验证过的代码直接给你。

这篇文章适合谁?刚接手 uniapp 项目、想搞清楚文件上传怎么做的新人;已经写完上传功能但被 Android、iOS 差异折腾到怀疑人生的中级开发者;以及想把手头上传代码整理成通用模块、减少重复劳动的老手。不管你用 Vue 2 还是 Vue 3 语法,核心 API 都是一套,封装思路也通用。

先说一个很多人问过我的问题:uniapp 做文件上传,为什么官方推荐用 uni.uploadFile,而不是 uni.request?答案很简单——uni.request 本质上是普通 HTTP 请求,你要传文件就得自己拼 FormData,把文件二进制塞进去,这在 APP 端的不同系统、不同 WebView 内核下表现差异很大,很容易出现格式不对、文件读不到、兼容性崩溃之类的问题。而 uni.uploadFile 是官方针对文件上传场景封装好的 API,它内部帮你处理了 multipart/form-data 的组装、文件流的读取、以及各端差异的抹平。你只需要告诉它文件路径、上传地址、字段名,剩下的它自己搞定。这也是它在跨端项目中成为唯一正统方案的原因。

1. 为什么是 uni.uploadFile:文件上传方案选型背后的那笔账

很多人在项目初期会纠结一个问题:文件上传嘛,用现成的 axios、fly.js 或者自己封装的 request 不就行了,为什么非要单独用一个 API?我最初也这么干过,但实际测试完 APP 端之后,老老实实换回了 uni.uploadFile。这一节我把几个方案的优缺点摊开讲,你以后选型会顺手很多。

1.1 直接使用 uni.request 模拟表单上传的老路与新坑

先说最直觉的方案:通过 uni.request 发起 POST 请求,在 data 里传一个 FormData 对象。这个思路在 H5 端确实能跑,因为浏览器环境下 FormData 是原生支持的对象,你把文件 Blob 或者 File 对象塞进去,浏览器会自动帮你生成 multipart 格式的请求体。但在 APP 端情况就变了,APP 端的运行环境不是浏览器,是 JSCore 或者 V8 引擎配合原生网络栈,FormData 对象虽然存在,但你从 uni.chooseImage 里拿到的并不是浏览器里的 File 对象,而是一个字符串路径,比如/storage/emulated/0/xxx.jpg或者file:///var/mobile/xxx.jpg。你没法直接把这个路径塞进 FormData。

即便你用了一些第三方库强行读取文件为 ArrayBuffer 再组装,也会遇到新的问题:Android 端某些机型对 ArrayBuffer 的处理结果不一致,文件体出现了乱码;iOS 端对二进制数据的类型标记有严格限制,后端收不到正确的 Content-Type。这种方案调试成本极高,剪不断理还乱。所以 uni.request 传文件这条路,我从实用性角度劝你直接放弃,除非你只做 H5 端,并且后端接口恰好能兼容你自己拼的 multipart 结构。

1.2 原生插件与第三方 SDK 的能力边界

还有人会想:能不能用 HTML5+ 的原生能力,或者干脆接入阿里云 OSS、七牛云这种 SDK?这个思路本身没错,大文件、分片上传、断点续传这些场景确实需要更强的底层能力。但前提是——你确定你的项目真的需要这些重型能力吗?

七牛、OSS 的官方 SDK 在 uniapp 里通常要配合原生插件使用,要么引入复杂的依赖,要么得处理临时密钥、Token 签名的逻辑。对一个常规业务来说,比如用户上传一张头像、提交一个身份证照片、导出一个 Excel,用 uni.uploadFile 就够了。我见过不少团队一上来就接 OSS,结果发现后端接口根本不支持直传,最后还是走自己的服务器中转,白白增加了一个月的开发量。方案选型永远是为业务服务的,不是越复杂越好。除非你存在几百 MB 级别的视频文件、弱网环境下的上传中断恢复需求,否则把 uni.uploadFile 用到极致,比引入重型 SDK 划算得多。

1.3 跨端一致性:为什么 APP、小程序、H5 共用一套代码能行

uni.uploadFile 最大的价值在于它的跨端一致性。同样一段代码,在微信小程序里上传文件走的是小程序的 uploadFile 接口,在 APP 端走的是原生网络栈,在 H5 端走的是 XMLHttpRequest。你在业务层写得一模一样,最终执行时由 uniapp 框架帮你适配了底层差异。这一点在团队协作中太重要了:后端只需要对接一套上传接口,前端在三种端上不需要维护三套上传逻辑。

另外,你还可以通过条件编译在 APP 端和 H5 端做一些差异化处理。比如在 APP 端需要对大图做压缩,在 H5 端可以直接把压缩参数塞给后端由后端处理;在 APP 端需要额外拼接设备信息到 formData,在 H5 端则不需要。这些逻辑都可以写在同一个函数里,配合#ifdef APP-PLUS#ifdef H5灵活切换,而不是复制三个文件去维护。跨端框架的意义就在于消灭重复劳动,你用它就要把它的特性用足。

2. 参数拆解与 APP 端专属坑位:把 uni.uploadFile 用明白

很多人用 uni.uploadFile 只填四个参数:url、filePath、name、success。这当然能跑通最简单的场景,但一遇到真实业务就露馅了:不知道 formData 是干什么的、header 里的 token 怎么带、怎么监听上传进度、Android 和 iOS 的路径差异怎么处理。这一节我把每个参数掰开揉碎讲清楚,包括官方文档没写明白的坑。

2.1 必填参数的深层理解与文件字段名的正确姿势

先看一张我整理的参数对照表,几乎涵盖了你日常开发会用到的所有字段:

参数名类型必填说明
urlString开发者服务器上传接口地址
filePathString要上传文件资源的本地路径
nameString文件对应的 key,后端通过它取文件
filesArray需要上传的文件列表,使用 files 时 filePath 和 name 失效
formDataObjectHTTP 请求中其他额外的 form 数据
headerObjectHTTP 请求 Header,比如 token
timeoutNumber超时时间,单位 ms,默认 60000
successFunction上传成功回调
failFunction上传失败回调
completeFunction上传结束回调(无论成功失败都会执行)

这里最容易被忽略的参数是 timeout。uni.uploadFile 默认超时时间是 60 秒,如果你上传的是大文件或者当前网络状况一般,极容易触发超时。我建议在大文件上传场景把它改成 120000 或更长,并且配合上传进度提示让用户知道系统还在工作,而不是干巴巴等一个失败提示。

不过这里有个非常常见的坑:上传接口返回的任何 HTTP 状态码,只要不是网络层报错,uni.uploadFile 都会进 success 回调。什么意思?就是如果你的接口业务校验失败返回 HTTP 400,前端依旧认为“上传成功”,只有在 success 回调里再解析 res.statusCode。我见过太多人只写了 success 回调就从 res.data 里取结果,结果拿到的是后端错误信息,还以为是自己代码写错了。严谨做法是:进 success 回调后先判断 statusCode 是否为 2xx,再处理业务数据。

name 字段也有很多讲究。它是表单字段名,也就是 multipart 数据里标识文件的 key。后端代码里一般会用request.files.get('file')或者$_FILES['file']这类写法取文件,这里的字符串必须和 name 一致。有人喜欢固定叫 file,有人喜欢叫 uploadFile,都不重要,但一定要提前和后端对齐。否则你传了半天文件,后端告诉你 field 不存在,这属于最简单的对接失误。

2.2 formData 与 header 的使用技巧:token、业务参数和隐藏的坑

formData 用来携带文件以外的业务字段,比如用户 ID、业务类型、备注说明等。这些字段会和文件一起以 multipart/form-data 格式提交到后端,后端可以从 request 参数里取值。我经常用它在头像上传时带一个 userId,在工单系统里带上 orderId 和 attachType,这样后端一个接口就能同时处理文件和关联业务信息,不需要先传文件再拿返回的文件 ID 去调另一个接口。

header 则用来携带鉴权信息。最常见的做法是在 header 里塞 token,比如header: { Authorization: 'Bearer ' + token }或者自定义一个token字段。有一个小坑是:如果你在 H5 端调试时发现自定义 header 带不过去,那不是 uniapp 的问题,是浏览器跨域限制。H5 端浏览器会先发一个 OPTIONS 预检请求,如果后端没做跨域配置,自定义 header 会被浏览器直接拦截。APP 端不存在这个限制,所以经常会遇到 H5 调不通、APP 调得通的诡异现象。解决方法是让后端在跨域配置里加Access-Control-Allow-Headers,把自定义 header 字段名加进去。

还有一点很多人不知道:当你通过 files 参数上传多文件时,filePath 和 name 都会失效,两个参数不能混用。files 的格式是一个数组,每个元素包含 name 和 uri(注意是 uri 不是 path):

files: [ { name: 'file1', uri: '/storage/emulated/0/xxx.jpg' }, { name: 'file2', uri: 'file:///var/mobile/xxx.png' } ]

我实际用下来,files 参数在 APP 端对多文件上传更友好,能让后端每个文件都有独立字段名,方便通过不同 name 区分。如果你用普通循环调用 uni.uploadFile 传多个文件,后端收到的是同一个 name 字段的多个文件,处理起来反而麻烦。

2.3 APP 端文件路径的故事:chooseImage 返回的 tempFilePath、tempFiles 和 Path 前缀

文件路径这件事儿,是 APP 端最容易出问题的隐藏雷区。你在 H5 端用 uni.chooseImage,拿到的是一个 Blob 对象或者 base64,你不需要关心物理路径。但 APP 端不一样,你拿到的是一个临时文件路径,Android 通常长这样/storage/emulated/0/Android/data/xxx/cache/xxx.jpg,iOS 长这样file:///var/mobile/Containers/Data/Application/xxx/tmp/xxx.jpg

直接把这个路径传给 uni.uploadFile 一般没问题,因为两者同源。但如果你对这个路径做了额外处理,比如你用了第三方图片裁剪库、压缩库,它返回的可能是相对路径、file:// 开头路径或者 base64 字符串。这时候你就得自己做个判断和转换。我写了个辅助函数来处理这个问题:

function normalizeFilePath(path) { if (!path) return '' // 兼容 base64 直接返回 if (path.indexOf('data:') === 0) return path // Android 可能没有 file:// 前缀,直接返回 if (path.indexOf('/') === 0) return path // iOS 有 file:// 前缀,保留 if (path.indexOf('file://') === 0) return path // 其他情况尝试补充前缀 return 'file://' + path }

还有一个容易踩的坑:tempFilePath 是临时文件,APP 端系统会在一定时间后清理这些文件。如果你选择了图片但没有立刻上传,而是让用户填写完表单再点提交,中间隔了十分钟,临时文件可能已经被系统回收了,上传时就会拿到一个不存在的路径,报文件读取失败。针对这种情况,我的经验是选择文件时就触发上传,或者把临时文件复制到持久化目录(uni.env.USER_DATA_PATH下的路径),再或者上传失败后提示用户重新选择一次文件。总之不要过度依赖临时路径的持久性。

3. 封装一个能直接上线的上传模块:从一锤子买卖到可持续交付

把 uni.uploadFile 直接写在页面里,跑通一个 Demo 很容易,但用在真实项目里就很痛苦。页面一多,每个页面都要处理 loading、错误提示、token 拼接、缩略图展示,代码全是重复的。再加上需求一变,上传逻辑要统一调整,你就要逐一去改每个页面。我强烈建议每个项目都抽一个完整的上传模块,下面的实现可以直接抄。

3.1 单文件上传的核心实现:一个考虑周全的 uploadFile 函数

先看核心函数,我把它取名为 uploadFile,放在/utils/upload.js里。它支持 token 自动注入、超时控制、进度回调、错误码归并。这个版本我用 Vue 2 风格的 Promise 写法,通用于 Vue 2 和 Vue 3:

// utils/upload.js import store from '@/store' /** * 单文件上传 * @param {Object} options * @param {String} options.url 上传地址 * @param {String} options.filePath 本地文件路径 * @param {String} options.name 文件字段名 * @param {Object} options.formData 额外表单数据 * @param {Object} options.header 自定义请求头 * @param {Number} options.timeout 超时时间,默认120秒 * @param {Function} options.onProgress 进度回调 (res) => {} * @returns {Promise} */ export function uploadFile(options) { return new Promise((resolve, reject) => { const token = store.state.user.token const header = { ...(token ? { Authorization: 'Bearer ' + token } : {}), ...(options.header || {}) } uni.uploadFile({ url: options.url, filePath: options.filePath, name: options.name || 'file', formData: options.formData || {}, header, timeout: options.timeout || 120000, success: (res) => { // 先判断 HTTP 状态码 if (res.statusCode >= 200 && res.statusCode < 300) { let data = res.data // 后端可能返回字符串,尝试解析成 JSON if (typeof data === 'string') { try { data = JSON.parse(data) } catch (e) { // 解析失败保持原样 } } resolve({ code: 0, data }) } else { reject({ code: res.statusCode, message: '上传失败,请稍后重试' }) } }, fail: (err) => { reject({ code: -1, message: err.errMsg || '上传失败' }) } }) }) }

这里有几个设计决策解释一下。token 自动从 store 里取并拼到 header,这样页面代码里就不用每次手动传 token,也避免遗漏。超时时间默认 120 秒,是权衡用户等待体验后的选择——太短大文件容易挂,太长用户会以为卡死了。成功回调中解析 JSON 是因为很多后端在 Content-Type 上写的是 text/plain,实际返回 JSON 字符串,前端统一转一下,后面数据处理会轻松很多。

还要注意 Promise 的 resolve 和 reject 只处理了一层封装,也就是把 uni 原有的回调风格转成了 Promise 风格,而没有把业务上的成功失败逻辑也混进来。业务结果判断应该在页面层完成,这样上传模块本身的职责单一,可复用到不同业务场景。

3.2 页面里的标准调用姿势:loading、进度条与多文件处理

在页面里调用这个函数,我习惯配合 uni.showLoading 和 uni.hideLoading 做基础反馈,同时在上传大文件时使用进度回调。看一个标准示例:

// pages/upload-demo.vue import { uploadFile } from '@/utils/upload.js' export default { data() { return { imagePath: '', progress: 0 } }, methods: { chooseImage() { uni.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: (res) => { this.imagePath = res.tempFilePaths[0] } }) }, handleUpload() { if (!this.imagePath) { uni.showToast({ title: '请先选择图片', icon: 'none' }) return } uni.showLoading({ title: '上传中...', mask: true }) uploadFile({ url: 'https://api.example.com/upload', filePath: this.imagePath, name: 'file', formData: { userId: 123, scene: 'avatar' }, onProgress: (res) => { this.progress = res.progress } }).then((res) => { uni.hideLoading() uni.showToast({ title: '上传成功', icon: 'success' }) }).catch((err) => { uni.hideLoading() uni.showToast({ title: err.message || '上传失败', icon: 'none' }) }) } } }

注意我在 chooseImage 里用了sizeType: ['compressed'],这是针对 APP 端图片上传的一个优化。原图一张可能 5MB,压缩后往往只有几百 KB,上传速度快很多,服务器存储压力也小很多。如果你对图片清晰度有硬性要求,再考虑选原图。大部分业务场景,压缩图完全够用,用户也感知不到清晰度差异。

多文件上传的做法,要看后端接口的设计。如果后端支持一次传多个字段(就是前面提到的 files 参数),可以直接用 files 方式。如果后端只支持单文件接口,那就在前端用 Promise 串行上传,或者用Promise.all并行上传。串行适合顺序要求严格的场景,比如必须第一张传完再传第二张;并行适合求快的场景。但并行要注意并发数量,比如一次传 9 张图,全部并行发出去很容易把弱网环境的带宽打满,导致一张都传不上去。我的经验是控制并发数在 3 个以内,或者干脆用串行加进度汇总。

3.3 大文件与特殊格式的策略:分片思路先知道

如果你要上传的视频超过 50MB,直接 uni.uploadFile 整包上传,大概率会遇到两个问题:一是超时,二是弱网环境下传半天突然断掉,一切从头再来。解决办法是分片上传。uni.uploadFile 本身不支持分片,但你可以自己实现:用uni.getFileSystemManager().readFile把文件读成 ArrayBuffer,按固定大小切片(比如每片 2MB),然后逐片调用 uni.uploadFile 上传,后台按片号拼接。

这个方案说起来简单,实现起来细节很多:要记录每片的 index、要处理乱序、要做进度汇总、要支持断点续传(记录已传片数)。在小程序端有官方wx.uploadFile也有类似分片能力,但 uniapp 跨端框架下没有统一封装,所以如果不是强需求,我建议先用直接上传 + 进度提示的方案,真的遇到大文件再考虑引入专门的分片插件或原生 SDK。做了好几个项目下来,我的体会是百分之八十的业务文件都在 10MB 以下,直接上传加上合理的超时设置,用户体验是可以接受的。

4. 实战中踩过的坑与排查记录:这些问题我一个个帮你趟过了

这一节是干货浓度最高的部分。下面整理的每一条,都是我在真实项目中遇到并排查清楚的问题。这些问题你在官网文档里看不到,只有自己踩一遍或者听踩过的人说,才能避开。

4.1 上传接口看不见 payload:Charles 抓包与 header 丢失之谜

有不少人问我:为什么我用开发者工具调试时,明明上传成功了,但在浏览器 Network 面板里看不到 FormData payload,只能看到 request headers?这其实不是代码 bug,而是开发工具对 multipart 请求的展示策略。multipart/form-data 的请求体是二进制流,许多代理工具和浏览器开发者工具默认不会直接格式化展示,你需要点开 Payload 或者 View Source 才能看到具体内容。如果你用的还是某些低版本工具,可能完全不显示二进制体,这并不代表请求里没有数据。

更隐蔽的一个情况是:后端同事用 Swagger 或 Postman 测试接口能收到文件,但 APP 传过去就收不到。这种问题大概率出在 header 上。你在 header 里塞了一个 Content-Type 自定义值,比如Content-Type: application/json,这会导致整个请求体被后端按 JSON 解析,而 multipart 的数据结构被破坏。正确做法是上传文件时永远不要手动设置 Content-Type,让系统自动按 multipart/form-data 生成。很多框架封装过头,统一在请求拦截器里加 Content-Type,上传文件时又没排除这份逻辑,就掉坑里了。排查方法很简单:抓包看请求头里 Content-Type 是不是multipart/form-data; boundary=xxx,不是的话就去掉你代码或拦截器里手动设置的 Content-Type。

4.2 Android 上传报 400、iOS 正常:跨端差异排查从这三步开始

如果同一个上传接口,iOS 上跑得好好的,Android 上却报 400,先别急着怀疑后端。我排查过几次,最典型的原因有三个。第一个是文件路径问题,Android 端的文件路径可能包含空格、中文、特殊字符,如果某些底层网络库没有对它做 URL 编码,上传时就会因路径错误导致请求失败。第二个是 Android 的 WebView 或网络栈对文件类型的识别方式不同,后端解析不到正确的 Content-Type 时会拒收。第三个是 Android 上存在部分 ROM 对 HTTPS 证书校验更严格,测试环境如果用的是自签名证书,Android 会直接拒绝连接,表现就是请求发不出去,也拿不到返回。

排查逻辑应该是顺序排除:先用 Postman 测接口本身,确认后端没问题;再用 H5 端在浏览器里测,确认 uniapp 侧代码逻辑没问题;最后才聚焦到 APP 端。如果是证书问题,在 manifest.json 里配置好正式证书即可,不要为了测试方便在正式包里解除证书校验,这是安全大忌。如果是路径问题,把文件路径打印出来,和后端沟通确认他收到的文件名是否乱码。这些排查思路能覆盖大部分 Android 上传异常的场景。

4.3 上传超过 10MB 必失败:从前端超时到后端限制的双层卡点

很多团队在上线后收到用户反馈:小图片传得上去,一传视频或大图就失败。这类问题绝大多数不是前端代码写错了,而是前后端两层限制叠加的结果。前端侧,uni.uploadFile 的默认 timeout 是 60 秒,弱网环境下传一个 5MB 的图片可能就要 40 秒,超过就报 timeout;后端侧,Nginx 默认client_max_body_size是 1MB,Tomcat 默认也有大小限制,你上传一个 10MB 的文件,请求刚到 Nginx 就被拒了,返回 413 Request Entity Too Large。

排查这类问题,你先看报错状态码。如果是 413,直接找后端或运维调 Nginx 配置;如果是前端 timeout 或者网络错误,检查 timeout 参数和后端接口响应速度。另一个容易被忽略的点是:很多 APP 抓包看请求一直 pending,实际上文件已经传完,但后端在处理文件时做了限速、压缩、病毒扫描等耗时操作,响应迟迟没返回,前端等不及就报错了。这种情况建议后端接口设计成两段式:第一段先返回“文件已接收”,第二段再异步处理文件业务逻辑。或者至少把 timeout 设置得足够大。

4.4 上传成功但后端拿不到文件:multipart 边界与字段对齐问题

还有一个出现频率极高的痛苦经历:前端明明显示上传成功,后端也收到了请求,但在代码里死活取不到文件。这个问题的根源通常是 multipart 数据解析问题,尤其是文件字段名对不上。前端传的 name 是 file,后端用 file 取没问题;但如果前端传的 name 是 upload,后端用 file 去取,自然取不到。这类问题最常见于前后端联调时没有先对齐字段名,等联调时才发现。

另一个更隐蔽的场景是:后端用的框架对 multipart 有大小限制,超过限制的字段会被静默丢弃。比如 PHP 的upload_max_filesize默认只有 2MB,Spring 的spring.servlet.multipart.max-file-size默认也只有 1MB。前端传了一个 5MB 的文件,后端框架解析时直接丢了,但 HTTP 层面已经返回 200,前端不知道,还以为成功了。排查方法:让后端同事在接口入口处打印所有请求参数和文件对象,看看 framework 到底有没有解析到文件。或者用抓包工具对比一下请求体大小和实际文件大小是否一致。很多看似诡异的问题,一层层剥开都是这些“众所周知”的限制在作祟。

4.5 APP 端常见问题速查表:从报错到解决方案的一页纸

最后整理一个速查表,方便将来遇到问题时快速定位:

问题现象可能原因排查与解决
上传报 timeout文件太大或弱网,默认60秒太短调大 timeout,加进度提示,考虑分片
上传成功但 res.data 是字符串后端响应 Content-Type 不标准前端 JSON.parse 兼容
选完图片等一会儿再传失败临时文件被系统清理选中后立即上传,或复制到持久目录
Android 偶发 400路径特殊字符、文件类型识别差异打印路径,检查 URI 编码,后端查日志
iOS 正常、Android 连不上测试环境证书问题正式环境用正规证书,测试环境临时信任
后端报 413Nginx/Tomcat 上传大小限制找运维调整 client_max_body_size
后端拿不到文件,前端却成功name 字段不一致,或框架大小限制对齐字段名,检查后端框架配置
header token 带不上H5 跨域限制APP 端不受影响;H5 后端加 Access-Control-Allow-Headers

这些问题的共性都是:前端报错信息往往只是表象,真正的根因分散在路径、协议、后端框架限制、跨域策略等各个层面。你只要牢记一条排查原则——先确认接口本身通不通(用 Postman 测),再确认请求体结构对不对(抓包看 payload),最后确认后端解析到没有(打印后端日志),八成问题都能快速找到答案。我见过太多人绕了半天圈,最后发现是后端少导了一个注解或者少写了一个配置。

5. 一点个人的习惯和建议:这些细节决定你的上传功能能跑多久

最后分享几个我长期养成的开发习惯,不一定适合所有人,但确实帮我减少了很多不必要的返工。

第一,做上传功能前,一定先列一个核对清单跟后端对齐:接口地址、请求方式、字段名 name、额外业务参数、返回数据格式、鉴权方式、文件大小限制。这七项对齐了,开发阶段基本不会因为联调返工。我见过太多项目,前端把上传写完了,后端说“我接口里字段叫 file,你传的怎么是 upload”,然后两边开始扯皮。如果一开始就用文档把这个确认清楚,这些时间全都省了。

第二,关于压缩策略,我的默认选择是 Android 和 iOS 都走sizeType: ['compressed'],除非业务明确要求原图。很多人担心压缩后清晰度不够,但实际在手机上展示,几乎看不出区别。而且压缩不仅省流量,也能让上传成功率大幅提升。如果你实在不放心,可以给用户一个“原图上传”的开关选项,默认压缩,用户需要时自己切原图。

第三,前端对错误信息要做“用户能看懂”的兜底。用户传文件失败时,你给他看“timeout of 120000ms exceeded”,他根本不知道什么意思。正确做法是把错误信息映射成“网络不太顺畅,请检查后重试”“文件过大,请压缩后上传”这类人话。实现上就是在 catch 里根据错误类型做文案映射,不能懒。

第四,尽可能把上传模块独立成 utils 文件,不要散落在页面里。一方面是因为项目迭代后往往会上传需求,比如增加水印、增加压缩、增加进度条,集中管理改一处就行;另一方面也方便在多个包之间复用。你如果接过旧项目,应该深有体会:一个上传逻辑在五个页面里被复制了五遍,每个页面改得还都不一样,那种维护体验有多崩溃。

我做 uniapp 三年多,上传功能看着简单,但它牵扯到客户端、网络、服务端三方协作,任何一个环节出问题,表现都是“传不上去”。希望这篇内容能帮你在做上传功能时少踩几个坑,哪怕只有一个细节对你有用,那也值了。如果你在实际项目里遇到了我这里没写到的问题,多半就是新坑,解决之后记下来,经验就是这么一点点攒起来的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询