- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
本文围绕 uni-app X 的
uni.uploadFile接口展开,系统讲解其调用方式、options 全部参数、返回的 UploadTask 对象、错误码体系,并结合本仓库中的 uts 源码与自动化测试页面,深入说明 Android/iOS/HarmonyOS/Web/微信小程序各端的上传实现原理与注意事项。读完本文,你将掌握如何在 uni-app X 工程中完成单文件上传、多文件上传、进度监听、中断上传、Cookie 携带与 User-Agent 定制,并能对照源码定位上传问题。
uni.uploadFile(options)用于将本地资源(图片、视频、文件等)上传到开发者服务器。它在 uni-app X 中位于网络模块uni-network(仓库内实现位于 src/uni_modules/uni-network),该模块在 package.json 中声明了request、uploadFile、downloadFile、configMTLS四个面向 App 端的扩展接口,其中uploadFile在 Android(Kotlin)、iOS(Swift)、HarmonyOS(ArkTS)均有原生实现。
官方推荐将文件上传到 uniCloud,uniCloud 提供了更便宜的 CDN 与更好的易用性;若使用自有服务器,则按本文所述方式将本地资源通过 multipart/form-data 表单提交到后端接口。
一、接口签名与兼容性
uni.uploadFile(options: UploadFileOptions): UploadTask在仓库源码 src/uni_modules/uni-network/utssdk/interface.uts 中,上传接口的类型定义如下:
export type UploadFile = (options: UploadFileOptions) => UploadTask;各端最低支持版本(对应文档 docs/api/upload-file.md 中的兼容性表格):
| Web | 微信小程序 | Android | iOS | HarmonyOS | | :- | :- | :- | :- | :- | | 4.0 | 4.41 | 3.91 | 4.11 | 4.61 |
说明:
unixVer(uni-app x 版本)在 Web 端为 4.0、微信小程序端为 4.41、Android 为 3.91、iOS 为 4.11、HarmonyOS 为 4.61;这些版本号同时出现在源码注释的@uniPlatform标记中,与文档保持一致。
二、options 参数详解
options的类型为UploadFileOptions,其完整定义见 interface.uts。全部参数如下:
| 名称 | 类型 | 必备 | 默认值 | 描述 | | :- | :- | :- | :- | :- | | url | string | 是 | 无 | 开发者服务器地址 | | filePath | string | 否 | null | 要上传文件资源的路径,支持uni.env| | name | string | 否 | "file" | 文件对应的 key,服务端通过该 key 读取文件二进制内容 | | files | Array<UploadFileOptionFiles> | 否 | null | 需要上传的文件列表 | | header | UTSJSONObject | 否 | null | HTTP 请求 Header,header 中不能设置 Referer | | formData | UTSJSONObject | 否 | null | HTTP 请求中其他额外的 form data | | timeout | number | 否 | 120000 | 超时时间,单位 ms | | success | (result: UploadFileSuccess) => void | 否 | null | 成功回调 | | fail | (result: UploadFileFail) => void | 否 | null | 失败回调 | | complete | (result: any) => void | 否 | null | 结束回调(成功、失败都会执行) | | enableHttp2 | boolean | 否 | 无 | 仅微信小程序(基础库 2.10.4+):是否开启 http2 | | enableProfile | boolean | 否 | 无 | 仅微信小程序(基础库 3.5.0+):是否开启 profile,iOS 与 Android 端默认开启,开启后可在 res.profile 中查看性能调试信息 | | enableQuic | boolean | 否 | 无 | 仅微信小程序(基础库 2.10.4+):是否开启 Quic/h3 协议 |
2.1 必填参数的源码校验
从协议层看,url是唯一必填参数。仓库 src/uni_modules/uni-network/utssdk/protocol.uts 中定义了UploadFileApiProtocol与UploadFileApiOptions:
- 协议声明中
url为required: true,filePath、name、header、formData、timeout均为可选; - 格式化阶段,若
url为空直接抛出'url is required'; - 若
name为空,则补默认值'file'。
这一逻辑与文档中的默认值表完全对应,即不传name时表单字段名固定为file。
2.2 files 数组元素 UploadFileOptionFiles
当需要一次上传多个文件时,使用files数组,其元素属性(interface.uts):
| 名称 | 类型 | 必备 | 默认值 | 描述 | | :- | :- | :- | :- | :- | | name | string | 否 | "file" | multipart 提交时的表单项目名。若 name 不填或填的值相同,服务端读取文件时可能只能读取到一个文件 | | uri | string | 是 | 无 | 要上传文件资源的路径 | | file | any | 否 | 无 | 要上传的文件对象(Web 端支持,App 端 Android/iOS 支持,HarmonyOS 暂不支持) |
2.3 filePath 与 uni.env
filePath支持uni.env中的环境变量路径。典型用法是先下载到应用私有目录,再上传:
const filePath = `${uni.env.USER_DATA_PATH}/uni-app.png` uni.downloadFile({ url: "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/uni-app.png", filePath: filePath, success: () => { uni.uploadFile({ url: 'https://unidemo.dcloud.net.cn/upload', filePath: filePath, name: 'file', success: () => { console.log('上传成功') }, fail: () => { console.log('上传失败') } }) } })需要注意:微信小程序只支持USER_DATA_PATH,且子目录未创建的情况下不能直接下载到子目录内(该注释同样出现在 src/pages/API/upload-file/upload-file.uvue 的自动化测试例中)。
三、成功与失败回调的结构
3.1 UploadFileSuccess
| 名称 | 类型 | 必备 | 描述 | | :- | :- | :- | :- | | data | string | 是 | 开发者服务器返回的数据 | | statusCode | number | 是 | 开发者服务器返回的 HTTP 状态码 | | profile | UploadFileSuccessProfile | 否 | 网络请求调试信息(微信小程序基础库 3.5.0+,iOS/Android 支持) |
profile中包含完整的网络性能时间线字段:fetchStart、requestStart、requestEnd、responseStart、responseEnd、connectStart、connectEnd、domainLookUpStart、domainLookUpEnd、SSLconnectionStart、SSLconnectionEnd、redirectStart、redirectEnd、rtt、throughputKbps、downstreamThroughputKbpsEstimate、httpRttEstimate、transportRttEstimate、protocol(http1.1/h2/quic/unknown)、peerIP、port、sendBytesCount、receivedBytedCount、socketReused、queueStart、queueEnd、invokeStart、usingHighPerformanceMode、estimate_nettype、httpDNSDomainLookUpStart、httpDNSDomainLookUpEnd等,可用于定位网络瓶颈与判断是否走了高性能模式。
3.2 UploadFileFail 与错误码
失败对象结构:
| 名称 | 类型 | 必备 | 描述 | | :- | :- | :- | :- | | errCode | number | 是 | 错误码 | | errSubject | string | 是 | 统一错误主题(模块)名称 | | data | any | 否 | 错误信息中包含的数据 | | cause | Error | 否 | 源错误信息(可包含多个错误,详见 SourceError) | | errMsg | string | 是 | 错误信息 |
errCode合法值及含义(与 src/uni_modules/uni-network/utssdk/unierror.uts 中的错误码表一一对应):
| errCode | 描述 | | :- | :- | | 5 | 接口超时 | | 1000 | 服务端系统错误 | | 100001 | json 数据解析错误 | | 100002 | 错误信息 json 解析失败 | | 100003 | json 解析类型转换失败 | | 600003 | 网络中断 | | 600008 | data 参数类型不合法 | | 600009 | URL 格式不合法 | | 600010 | Cronet 模块加载失败 | | 602001 | request 系统错误 |
其中5(超时)与1000(服务端系统错误)并非原生层直接返回,而是在 app-android/index.uts 的监听器中对原始错误信息做二次映射得到的:错误信息包含timeout时归为5,cause含Connection refused时归为1000,含Network is unreachable时归为600003,含invalid URL时归为600009。iOS 端实现位于 app-ios/index.uts,HarmonyOS 端实现位于 app-harmony/index.uts,错误处理语义保持一致。
四、返回值 UploadTask 与上传生命周期
uni.uploadFile返回一个UploadTask对象(interface.uts),用于控制上传任务:
| 方法 | 签名 | 描述 | | :- | :- | :- | | abort | abort(): void | 中断上传任务 | | onProgressUpdate | onProgressUpdate(callback: UploadFileProgressUpdateCallback): void | 监听上传进度变化 |
onProgressUpdate的回调参数OnProgressUpdateResult:
| 名称 | 类型 | 描述 | | :- | :- | :- | | progress | number | 上传进度百分比 | | totalBytesSent | number | 已经上传的数据长度(Bytes) | | totalBytesExpectedToSend | number | 预期需要上传的数据总长度(Bytes) |
从 Android 实现看,UploadController.uploadFile(src/uni_modules/uni-network/utssdk/app-android/network/upload/UploadController.uts)使用 OkHttp 的MultipartBody构建 multipart/form-data 请求体,通过ProgressRequestBody包装实现进度上报,并在NetworkUploadProgressListener.onProgress中按(bytesWritten / contentLength) * 100计算百分比,同时给出已发送字节数与总字节数。abort()对应底层call.cancel(),onProgressUpdate则把回调注册进监听器的progressListeners列表,由NetworkUploadFileListener.onProgress分发。
4.1 iOS 4.25 版本注意事项:Task 对象自动销毁
在 4.25 版本起,iOS 平台增加了 Task 原生对象自动销毁的逻辑:上传完成后会自动释放原生的 Task 对象。建议开发者在complete回调中置空 Task 对象:
complete: () => { this.task = null }如果不释放,之后继续调用 Task 对象的方法会导致控制台报错:error: instance object does not exist: id:15。
五、完整示例:选择图片并上传
以下示例取自 hello uni-app x 的官方演示(仓库内对应页面为 src/pages/API/upload-file/upload-file.uvue,模板部分见 docs/api/upload-file.md),展示"选择图片 → 显示加载态 → 上传 → 展示结果 → 释放 Task"的完整闭环:
<script setup lang="uts"> type DataType = { title: string; imageSrc: string; task: UploadTask | null; } const data = reactive({ title: 'uploadFile', imageSrc: '', task: null, } as DataType) onUnload(() => { data.imageSrc = ''; uni.hideLoading(); data.task?.abort(); }) const chooseImage = () => { uni.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album'], success: (res) => { console.log('chooseImage success, temp path is', res.tempFilePaths[0]) var imageSrc = res.tempFilePaths[0] uni.showLoading({ title: '上传中' }) data.task = uni.uploadFile({ url: 'https://unidemo.dcloud.net.cn/upload', //仅为示例,非真实的接口地址 filePath: imageSrc, name: 'file', formData: { 'user': 'test' }, success: (res) => { console.log('uploadImage success, res is:', res) uni.showToast({ title: '上传成功', icon: 'success', duration: 1000 }) data.imageSrc = imageSrc }, fail: (err) => { console.log('uploadImage fail', err); uni.showModal({ content: err.errMsg, showCancel: false }); }, complete: (res) => { uni.hideLoading(); data.task = null } }); }, fail: (err) => { console.log('chooseImage fail', err) } }) } </script>关键点:
- 在
onUnload中调用data.task?.abort()中断未完成的上传,防止页面退出后请求仍在进行; - 在
complete中置空data.task,符合 iOS 4.25 起 Task 自动释放的要求; fail回调中通过err.errMsg展示错误,便于用户理解失败原因。
六、进阶实战:多文件上传与请求头定制
6.1 多文件上传(files)
一次上传多个文件时使用files数组,务必为每个元素指定不同的name,否则服务端可能只能读取到一个文件:
uni.uploadFile({ url: 'https://unidemo.dcloud.net.cn/upload', files: [ { name: "file1", uri: imageSrc } as UploadFileOptionFiles, { name: "file2", uri: imageSrc } as UploadFileOptionFiles ], success: (res: UploadFileSuccess) => { if (res.statusCode == 200) { console.log('上传成功') } }, fail: () => { console.log('上传失败') }, })从 Android 源码 UploadController.uts 可以看到,files与filePath是互斥的两条分支:优先遍历files数组逐个构造addFormDataPart(name, fileName, requestBody);若files为空再回退到filePath分支。任一文件的路径非法(无法打开输入流)都会直接触发fail(错误信息为 "Illegal file")。
6.2 定制 User-Agent
header支持自定义请求头,例如覆盖User-Agent。仓库测试页面中的jest_uploadFileVerifyUA验证了该能力:上传到回显接口后,从响应 JSON 中取出requestHeaders['user-agent'],校验其数量为 1(即自定义值生效且未追加默认 UA)。
对应 Android 实现 UploadController.uts:遍历header时检测是否已含User-Agent(忽略大小写),若开发者未提供则自动补上 WebView 的 UA。因此当你在header中指定"User-Agent": "custom"时,最终请求只会携带这一个 UA 值。
uni.uploadFile({ url: 'https://request.dcloud.net.cn/api/http/header/upload', header: { "User-Agent": "custom" }, formData: { 'user': 'test' }, success: (res: UploadFileSuccess) => { // 从 res.data 解析出 requestHeaders,校验 user-agent } })注意:header 中不能设置 Referer。
6.3 仅携带 formData、不传文件
uploadFile也允许不传任何文件、仅提交表单字段(仓库测试例jest_uploadFileWithoutFile即为此场景)。但 Android 端源码 UploadController.uts 规定:formData、filePath、files三者必须至少提供一个,否则直接以错误码602001(request system error)结束任务。
七、Web 端上传的特殊限制
Web 端(uni-app x 的unixVer4.0 起支持)上传文件时,只能使用 downloadFile、chooseImage 等返回文件对象(File 对象)的接口返回值作为要上传的文件,即通过files[].file传入文件对象,而不能直接传磁盘路径字符串。这一点体现在UploadFileOptionFiles.file字段的兼容性上:该字段 Web 端为 4.0 起支持,而uri字段 Web 端标记为x(不支持)。
八、底层实现一览(源码导读)
uni.uploadFile在 App 端由 uts 插件uni-network提供原生能力,各端入口与关键文件如下:
| 平台 | 入口 | 核心实现 | | :- | :- | :- | | Android | app-android/index.uts | network/upload/UploadController.uts(OkHttp MultipartBody) | | iOS | app-ios/index.uts | network/upload/UploadController.uts | | HarmonyOS | app-harmony/index.uts | network/uploadFile.uts | | 类型与协议 | interface.uts、protocol.uts | 参数默认值、必填校验、回调类型定义 | | 错误码 | unierror.uts | errCode 与错误主题映射 |
从源码结构看,Android 端上传流程可概括为:
uploadFile()通过NetworkManager.getInstance().uploadFile(options, listener)创建任务(app-android/index.uts);UploadController依据timeout(默认 120000ms)配置 OkHttp 的 connect/read/write 三段时间,并注入CookieInterceptor以支持 Cookie 携带;- 用
MultipartBody.Builder依次写入formData键值对与文件 part(InputStreamRequestBody支持content://、file://、unifile://、相对路径与 android_asset 等路径形式); ProgressRequestBody包装请求体以计算上传进度,异步call.enqueue()发起请求;- 监听器把结果通过 Handler 抛回主线程,触发
success/fail/complete回调。
仓库中同时提供了针对上述能力的自动化测试用例(src/pages/API/upload-file/upload-file.test.js 与 upload-file.uvue 内的jest_*系列方法),覆盖单文件上传、uni.env 路径上传、Cookie 携带与清除、多文件上传、UTS 模块调用、无文件上传、UA 定制等场景,可作为回归验证参考。
九、总结与最佳实践
- 上传前先通过
uni.chooseImage/uni.chooseVideo等接口拿到本地资源路径或文件对象,再交给uni.uploadFile; - 必传
url,并按需设置name(默认file)、formData、timeout;多文件上传使用files并保证name唯一; - 善用
UploadTask:onProgressUpdate展示上传进度条,abort()在页面卸载或用户取消时中断任务; - 在
complete中置空 Task 引用,规避 iOS 4.25 起 Task 自动销毁带来的instance object does not exist报错; - 结合
fail回调中的errCode(超时 5、URL 非法 600009、网络中断 600003、系统错误 602001 等)快速定位问题; - Web 端只能上传由 downloadFile/chooseImage 等接口返回的文件对象;
- 若追求更低的存储与 CDN 成本、更简单的接入体验,可优先考虑上传到 uniCloud。
- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
相关推荐
uni-app x uni.scanCode 扫码 API 完全指南:参数、跨端实现原理与实战示例
uni app x uni.scanCode 扫码 API 完全指南:参数、跨端实现原理与实战示例 uni app x 提供的 uni.scanCode 是调用
示例工程前端移动开发跨平台uni-app x 跨端文件选择实战:uni.chooseFile API 全参数解析与多平台实现原理
uni app x 跨端文件选择实战:uni.chooseFile API 全参数解析与多平台实现原理 uni.chooseFile 是 uni app / u
示例工程前端移动开发跨平台uni-app x 消息提示框完全指南:uni.showToast 与 uni.hideToast 参数详解与跨端实现原理
uni app x 消息提示框完全指南:uni.showToast 与 uni.hideToast 参数详解与跨端实现原理 uni app x 中 uni.sh
示例工程前端移动开发跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考