更新记录

1.0.3(2026-11-04) 下载此版本

修复了一些问题

1.0.2(2026-03-25) 下载此版本

优化移动端返回路径

1.0.1(2026-02-24) 下载此版本

新增x-file-picker 组件

查看更多

平台兼容性

uni-app(4.0)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ √ √ √ - √ √ √
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
√ √ √ √ √ √ √ √ √ √ √ √

x-native-uploader 原生文件上传组件

x-native-uploader 是一个跨平台的文件上传组件,专为解决 UniApp 在 App 端文件选择与上传的痛点而设计。它集成了 Native.js 的能力,支持在 Android 和 iOS 上调用原生文件选择器,同时兼容 H5 和小程序平台。

功能特性

  • 📱 原生体验: App 端使用 Native.js 调用系统原生文件选择器 (Intent / UIDocumentPicker),支持选择任意类型文件。
  • 🚀 高性能上传: 默认使用 uni.uploadFile,进度回调连续;可切换到 plus.uploader 原生上传(适合超大文件后台传输)。
  • 🔄 跨平台兼容: 自动降级处理,H5 使用 uni.chooseFile,小程序使用 uni.chooseMessageFile,一套代码多端运行。
  • 📦 多选支持: 支持一次选择多个文件进行批量上传 (Android/iOS/H5/MP),并受 limit 约束。
  • 🛠️ 灵活配置: 支持自定义上传 URL、Header、FormData 以及自定义触发按钮 UI。
  • 📄 PDF 及其他文件支持: 通过 ContentResolver 把 Android 的 content:// URI 落盘到应用沙盒再上传,彻底规避 file not found。
  • 🗑️ 单条管理: 每个文件支持删除、取消、失败重试,删除时自动中止任务并清理临时文件。

引入组件

import xNativeUploader from '@/components/x-native-uploader/x-native-uploader.vue';

export default {
  components: {
    xNativeUploader
  }
}

代码演示

基础用法 (单选)

默认点击按钮选择单个文件并自动上传。

<x-native-uploader
  url="https://your-api.com/upload"
  :header="{ 'Authorization': 'Bearer token' }"
  :formData="{ userId: '123' }"
  @success="onSuccess"
  @fail="onFail"
></x-native-uploader>

多文件上传

设置 multiple 属性开启多选,limit 控制最大选择数量。

<x-native-uploader
  url="https://your-api.com/upload"
  :multiple="true"
  :limit="9"
  fileType="all"
  @success="onSuccess"
  @fail="onFail"
>
  <view class="custom-btn">
    <text>点击批量上传文件</text>
  </view>
</x-native-uploader>

PDF 文件上传(Android 兼容)

设置 fileType="all" 即可支持上传 PDF 文件,组件会把 Android 平台的 content:// URI 落盘后再上传:

<x-native-uploader
  url="https://your-api.com/upload/pdf"
  fileType="all"
  trigger-text="上传 PDF 文件"
  @success="handlePdfUploadSuccess"
  @fail="handleUploadFail"
>
</x-native-uploader>

自定义触发按钮

通过默认插槽 (slot) 自定义上传按钮样式。

<x-native-uploader url="...">
  <button type="primary">点击上传</button>
</x-native-uploader>

API

Props

参数 说明 类型 默认值
url 上传接口地址 (必填) String -
name 上传文件的字段名 String 'file'
header 请求头对象 Object {}
formData 额外的上传参数 Object {}
fileType 文件选择类型,可选值: image, video, all String 'image'
multiple 是否开启多选 Boolean false
limit 最大文件数量 (含已上传的,超出会提示) Number 9
autoUpload 选择文件后是否自动上传 Boolean true
disabled 是否禁用选择 Boolean false
triggerText 默认触发按钮文案 String '选择文件并上传'
nativeUpload 是否用 plus.uploader 原生上传(默认 uni.uploadFile,进度更连续) Boolean false

Events

事件名 说明 回调参数
success 上传成功时触发 { file: FileObj, response: Any, raw: Object }
fail 上传失败时触发 { file: FileObj, error: Any }
progress 上传进度更新时触发 { file: FileObj, progress: Number, percent: Number }(progress 为整体进度,percent 为纯上传进度)
remove 删除列表中的某个文件时触发 { file: FileObj, index: Number }

Methods(通过 ref 调用)

方法 说明
clear() 中止所有上传并清空文件列表

FileObj 数据结构

{
  uid: 1,            // 唯一标识(列表 key,删除后不会错位)
  path: "...",       // 原始路径,可能是 content://
  localPath: "...",  // 实际用于上传的本地绝对路径(沙盒副本)
  uploadName: "...", // 落盘并随请求发送的文件名
  name: "...",       // 列表展示的文件名(保留用户选择的原名)
  size: 10240,       // 字节数
  progress: 100,     // 进度 (0-100),含准备阶段
  status: "success", // 状态: pending, uploading, success, error, cancelled
  statusText: "..."  // 状态描述文本
}

success 回调数据结构

onSuccess(e) {
  e.file;     // 上传完成后的 FileObj
  e.response; // 服务端返回体,已自动 JSON.parse;解析失败则是原始字符串
  e.raw;      // uni.uploadFile 的原始返回 { data, statusCode, errMsg }
}

真实回调示例(App 端选择 Excel 上传):

{
    "file": {
        "uid": 1,
        "path": "content://media/external/file/35",
        "localPath": "/storage/emulated/0/Android/data/io.dcloud.HBuilder/apps/HBuilder/doc/uploader/终端产品线-花名册.xlsx",
        "uploadName": "终端产品线-花名册.xlsx",
        "name": "终端产品线-花名册.xlsx",
        "size": 8399,
        "progress": 100,
        "status": "success",
        "statusText": "上传成功",
        "error": ""
    },
    "response": {
        "strings": {
            "userId": "1001",
            "source": "app"
        },
        "error": "0",
        "files": {
            "file": {
                "name": "终端产品线-花名册.xlsx",
                "url": "files/终端产品线-花名册.xlsx",
                "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                "size": 8399
            }
        }
    },
    "raw": {
        "data": "{\"strings\":{\"userId\":\"1001\",\"source\":\"app\"},\"error\":\"0\",\"files\":{\"file\":{\"name\":\"终端产品线-花名册.xlsx\",\"url\":\"files/终端产品线-花名册.xlsx\",\"type\":\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\"size\":8399}}}",
        "statusCode": 200,
        "errMsg": "uploadFile:ok"
    }
}

说明:

  • response 已经是对象,业务判断直接写 if (e.response.error === '0'),不用再 JSON.parse(e.response)。
  • raw.statusCode 在 200~299 之间才会触发,其余状态码和网络错误都走 @fail。
  • response.strings 是服务端回显的 formData,response.files 是服务端保存的文件信息。
  • 服务端 multipart 收到的 filename 与 file.uploadName 一致;落盘时保留原始文件名,仅在同名冲突时追加 6 位后缀(如 花名册_112006.xlsx)。

注意事项

  1. Android 权限: App 端需要 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。
  2. iOS 配置: iOS 端无需特殊配置,使用 UIDocumentPickerViewController。
  3. H5 跨域: 确保上传接口支持 CORS 跨域请求。
  4. 小程序: 微信小程序使用 uni.chooseMessageFile,支持从聊天记录中选择文件。
  5. Android content:// URI 处理: 组件不再把 content:// 直接丢给上传器,而是通过 ContentResolver.openInputStream() 把文件内容复制到应用沙盒的 _doc/uploader/ 目录,用真实文件路径上传,因此不会出现 file not found。上传成功后会自动清理沙盒里的临时副本。
  6. 文件格式支持: 当 fileType="all" 时,支持选择任意类型文件,但上传前请确保服务器端支持该文件格式。文件名缺少扩展名时会根据 MIME 自动补全(如 application/pdf → .pdf)。

常见问题

Q: 为什么在 Android 上选择 PDF 文件后上传失败(file not found)?

A: 因为系统返回的是 content:// 形式的 URI,它不是真实文件路径,Android 10+ 的分区存储下普通上传器也读不了。组件现在的处理流程是:

  1. x-file-picker 返回 content:// 原始 URI 和文件名;
  2. x-native-uploader 用 ContentResolver 打开输入流,分块复制到 _doc/uploader/;
  3. 用沙盒里的真实路径上传,成功后自动删除副本。

如果仍然失败,请检查:

  1. 是否在真机上测试(模拟器的 content:// 兼容性较差);
  2. 查看控制台 [x-native-uploader] 开头的日志,确认落盘是否成功;
  3. 服务器端是否支持该文件格式及大小。

Q: 进度条为什么之前一直不动?

A: plus.uploader 的 statechanged 事件只在上传状态切换时触发,拿不到连续的字节进度,所以进度会从 0 直接跳到 100。现在默认使用 uni.uploadFile(onProgressUpdate 每帧回调),并保留了 nativeUpload 模式(内部用定时器轮询 uploadedSize/totalSize 补齐进度)。

Q: 为什么选完文件要点一下才显示进度?

A: Android 的 content:// 文件需要先复制到应用沙盒才能上传,大文件复制时会占用主线程。组件现在的处理方式是:

  1. 先把状态切成「准备文件中...」并渲染到屏幕上(不会出现无反馈的卡顿);
  2. 复制时按 40ms 时间片让出主线程,状态同步更新成「准备文件中 65%」;
  3. 复制完成后立刻切到「上传中 x%」。

进度条在准备阶段占 0~30%,上传阶段占 30~100%,全程只增不减。@progress 回调里的 progress 是整体进度,percent 是纯上传进度。

Q: 可以删除单个文件吗?

A: 可以。列表每一项右侧都有「删除」按钮,删除时会自动中止正在进行的上传任务、清理沙盒副本,并触发 remove 事件。上传失败的项还可以点「重试」,上传中的项可以点「取消」。

Q: 如何限制只能上传特定类型的文件?

A: 可以通过设置 fileType 属性来限制文件类型:

  • fileType="image" - 仅允许选择图片
  • fileType="video" - 仅允许选择视频
  • fileType="all" - 允许选择所有类型文件

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议