更新记录

1.0.0(2026-04-29) 下载此版本

更新日志

[1.0.0] - 2025-04-29

新增


平台兼容性

uni-app(4.42)

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

uni-file-upload-plugin

一个用于 uni-app 的 Android 文件选择插件,完美解决小米等 Android 手机 content:// URI 访问限制问题。

特性

  • ✅ 支持 Android 平台原生文件选择
  • ✅ 自动处理 content:// URI 转换(三重复制方案)
  • ✅ 支持所有常见文件类型(PDF、Office、图片、音频等)
  • ✅ 零依赖,纯原生实现
  • ✅ 完美兼容小米、华为、OPPO、VIVO 等主流手机
  • ✅ 完善的错误处理和权限管理

平台支持

平台 支持情况 说明
Android ✅ 完全支持 使用原生 Android API,三重复制方案
iOS ❌ 不支持 建议使用 xe-upload 等第三方组件
H5 ❌ 不支持 建议使用 uni.chooseFile
小程序 ❌ 不支持 建议使用 uni.chooseFile

为什么需要这个插件?

在 Android 平台上,从系统文件选择器获取的文件 URI 通常是 content:// 格式,这种 URI 在某些手机(特别是小米)上存在安全访问限制,导致 uni.uploadFile 无法直接读取文件内容。

本插件通过三重复制方案将 content:// URI 指向的文件复制到应用缓存目录,完美解决这个问题:

  1. 方案 1:FileChannel.transferTo(OS 内核级复制,兼容鸿蒙)
  2. 方案 2:NIO Files.copy(Android 8.0+ / API 26+)
  3. 方案 3:InputStream.transferTo(Java 9+ / Android 8.0+)

安装

方式一:本地导入

  1. uni-file-upload-plugin 文件夹复制到项目根目录
  2. 在需要使用的页面中导入:
import {
  selectFileAndroid,
  getMimeTypesFromExtensions,
  requestAndroidStoragePermission,
} from '@/uni-file-upload-plugin/index.js';

方式二:DCloud 插件市场

(待发布)

快速开始

基础用法

import {
  selectFileAndroid,
  getMimeTypesFromExtensions,
  requestAndroidStoragePermission,
} from '@/uni-file-upload-plugin/index.js';

// 选择文件
async function selectFile() {
  try {
    // 1. 请求权限
    const hasPermission = await requestAndroidStoragePermission();
    if (!hasPermission) {
      uni.showToast({ title: '需要文件访问权限', icon: 'none' });
      return;
    }

    // 2. 选择文件
    const mimeTypes = getMimeTypesFromExtensions(['.pdf', '.doc', '.docx']);
    const result = await selectFileAndroid(mimeTypes);

    console.log('文件路径:', result.filePath);
    console.log('文件名:', result.fileName);

    // 3. 上传文件
    uni.uploadFile({
      url: 'https://your-api.com/upload',
      filePath: result.filePath,
      name: 'file',
      success: (res) => {
        console.log('上传成功:', res);
      },
    });
  } catch (error) {
    console.error('选择文件失败:', error);
  }
}

完整示例

<template>
  <view>
    <button @click="selectAndUploadFile">选择并上传文件</button>
  </view>
</template>

<script setup>
  import {
    selectFileAndroid,
    getMimeTypesFromExtensions,
    requestAndroidStoragePermission,
  } from '@/uni-file-upload-plugin/index.js';

  const selectAndUploadFile = async () => {
    try {
      // 请求权限
      const hasPermission = await requestAndroidStoragePermission();
      if (!hasPermission) return;

      // 定义允许的文件类型
      const extensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.jpg', '.png'];
      const mimeTypes = getMimeTypesFromExtensions(extensions);

      // 选择文件
      const { filePath, fileName } = await selectFileAndroid(mimeTypes);

      // 上传文件
      uni.showLoading({ title: '上传中...' });
      uni.uploadFile({
        url: 'https://your-api.com/upload',
        filePath: filePath,
        name: 'file',
        formData: {
          fileName: fileName,
        },
        success: (res) => {
          uni.hideLoading();
          uni.showToast({ title: '上传成功', icon: 'success' });
        },
        fail: (err) => {
          uni.hideLoading();
          uni.showToast({ title: '上传失败', icon: 'none' });
        },
      });
    } catch (error) {
      console.error('操作失败:', error);
      uni.showToast({ title: '操作失败', icon: 'none' });
    }
  };
</script>

API 文档

selectFileAndroid(mimeTypes)

选择文件(Android 平台)

参数:

  • mimeTypes (Array): MIME 类型数组,如 ['application/pdf', 'image/jpeg']

返回值:

  • Promise<Object>:
    • filePath (String): 本地文件路径
    • fileName (String): 文件名

示例:

const result = await selectFileAndroid(['application/pdf']);
console.log(result.filePath); // /data/user/0/.../cache/temp_file_xxx
console.log(result.fileName); // document.pdf

getMimeTypesFromExtensions(extensions)

将文件扩展名转换为 MIME 类型

参数:

  • extensions (Array): 扩展名数组,如 ['.pdf', '.doc', 'docx']

返回值:

  • Array<String>: MIME 类型数组

支持的扩展名:

  • 文档:pdf, doc, docx, xls, xlsx, ppt, pptx, txt
  • 图片:jpg, jpeg, png, gif, webp
  • 音频:mp3, wav, m4a, aac, ogg, flac, opus

示例:

const mimeTypes = getMimeTypesFromExtensions(['.pdf', '.doc', '.jpg']);
// 返回: ['application/pdf', 'application/msword', 'image/jpeg']

requestAndroidStoragePermission()

请求 Android 文件访问权限

返回值:

  • Promise<Boolean>: 是否授权成功

示例:

const hasPermission = await requestAndroidStoragePermission();
if (hasPermission) {
  // 继续文件操作
}

copyContentUriToTemp(uri, fileName)

将 content:// URI 复制到临时目录(内部方法,一般不需要直接调用)

参数:

  • uri (Object): Android URI 对象
  • fileName (String): 文件名

返回值:

  • Promise<String>: 本地文件绝对路径

常见问题

1. 为什么选择文件后上传失败?

确保已经调用 requestAndroidStoragePermission() 请求权限。

2. 支持哪些文件类型?

支持所有常见文件类型,包括:

  • 文档:PDF, Word, Excel, PowerPoint, TXT
  • 图片:JPG, PNG, GIF, WebP
  • 音频:MP3, WAV, M4A, AAC, OGG, FLAC, OPUS

3. 为什么不支持 iOS?

iOS 平台的文件访问机制与 Android 不同,建议使用 xe-upload 等成熟的第三方组件。

4. 文件会保存在哪里?

文件会临时复制到应用缓存目录(getCacheDir()),上传完成后可以手动删除。

5. 如何限制文件大小?

在上传前检查文件大小:

const { filePath } = await selectFileAndroid(mimeTypes);

// 使用 plus.io 获取文件大小
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
  entry.file((file) => {
    if (file.size > 10 * 1024 * 1024) {
      // 10MB
      uni.showToast({ title: '文件不能超过10MB', icon: 'none' });
      return;
    }
    // 继续上传
  });
});

技术原理

三重复制方案

  1. FileChannel.transferTo

    • OS 内核级复制,性能最优
    • 兼容鸿蒙系统
    • 适用于大部分 Android 设备
  2. NIO Files.copy

    • Android 8.0+ (API 26+) 支持
    • 标准 Java NIO API
    • 兼容性好
  3. InputStream.transferTo

    • Java 9+ / Android 8.0+ 支持
    • 最后的兜底方案
    • 确保所有设备都能正常工作

插件会依次尝试这三种方案,直到成功复制文件。

更新日志

v1.0.0 (2026-04-29)

  • 🎉 首次发布
  • ✅ 支持 Android 平台文件选择
  • ✅ 三重复制方案处理 content:// URI
  • ✅ 完善的权限管理
  • ✅ 支持所有常见文件类型

许可证

MIT License

作者

Your Name

反馈与贡献

如有问题或建议,欢迎提交 Issue 或 PR。

隐私、权限声明

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

android.permission.READ_EXTERNAL_STORAGE(读取外部存储,用于访问用户选择的文件) android.permission.WRITE_EXTERNAL_STORAGE(写入外部存储,用于将文件复制到应用缓存目录)

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

本插件不采集任何用户数据,不向任何服务器发送数据。插件仅在本地执行文件选择和复制操作,所有数据处理均在用户设备本地完成。

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

许可协议

MIT License

Copyright (c) 2025 Your Name

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

暂无用户评论。