uni-app openDocument 文件打开能力全解析:uni-openDocument UTS 插件原理与实战
2026/9/20 12:41:41 网站建设 项目流程

uni-app openDocument 文件打开能力全解析:uni-openDocument UTS 插件原理与实战

【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app

uni-openDocument 是 uni-app 官方开源仓库中负责「打开文件」能力的 UTS 插件,它通过uni.openDocument()接口在 App(Android/iOS/鸿蒙)与微信小程序等平台上唤起系统或宿主应用预览/打开本地文档。本文将以仓库内 src/uni_modules/uni-openDocument 为核心,结合其完整源码与官方示例页,系统讲解 API 参数、错误码规范、各平台底层实现原理(Android Intent/FileProvider、iOS QuickLook、鸿蒙 Want 拉起)、UTS 插件机制与实战用法,帮助开发者正确集成并深度理解这一能力的跨端工作方式。

一、插件定位:一个用 UTS 封装原生能力的最小 uni_modules 插件

uni-openDocument 在仓库中的完整形态是一个标准的 uni_modules UTS 插件,目录结构如下(全部源码均在仓库内):

src/uni_modules/uni-openDocument/ ├── readme.md # 插件说明(本文关联文档) ├── package.json # 插件元数据、平台声明、依赖声明 ├── changelog.md # 版本变更记录 └── utssdk/ ├── interface.uts # API 类型定义:参数、回调、错误码 ├── protocol.uts # 常量:API 名称 'openDocument' ├── unierror.uts # 统一错误对象实现(UniError 子类) ├── app-android/ # Android 平台实现(UTS/Kotlin) │ ├── config.json # hooksClass 声明 │ └── index.uts ├── app-ios/ # iOS 平台实现(UTS/Swift) │ └── index.uts └── app-harmony/ # 鸿蒙平台实现(UTS/ArkTS) └── index.uts

从 package.json 可以看到它如何被注册为 uni-app 扩展 API:

{ "id": "uni-openDocument", "version": "1.0.0", "engines": { "HBuilderX": "^3.6.8" }, "uni_modules": { "dependencies": ["uni-fileSystemManager"], "uni-ext-api": { "uni": { "openDocument": { "name": "openDocument", "app": { "js": false, "kotlin": true, "swift": true, "arkts": true } } } } } }

其中uni-ext-api声明了该插件向全局uni对象注入名为openDocument的扩展 API,并指明 App 端分别在 Kotlin(Android)、Swift(iOS)、ArkTS(鸿蒙)三个编译目标上提供实现,而js: false表示它并非纯 JS 实现;dependencies声明依赖 uni-fileSystemManager(Android 端复制文件时会用到文件系统能力,见下文源码分析)。

二、API 快速上手:参数与回调

插件的对外类型定义全部集中在 utssdk/interface.uts,其中OpenDocumentOptions(interface.uts 第 371-712 行)定义了完整参数:

| 参数 | 类型 | 必填 | 说明 | | -- | -- | -- | -- | |filePath|string| 是 | 文件路径,仅支持本地路径(临时路径、静态资源路径、缓存目录路径等) | |fileType|string \| null| 否 | 文件类型(扩展名),如docpdf。微信小程序仅支持doc, xls, ppt, pdf, docx, xlsx, pptx;App 端由系统打开,原则上可打开任意文件 | |success|(res: OpenDocumentSuccess) => void| 否 | 调用成功回调,成功时res为空对象{}| |fail|(res: OpenDocumentFail) => void| 否 | 调用失败回调,携带errCode/errMsg| |complete|(res: any) => void| 否 | 调用结束回调,成功、失败都会执行 |

Uni接口中的方法声明(interface.uts 第 715-788 行)明确了openDocument(options?)的调用形态,并标注其支持 Vue2 与 Vue3 两种工程。最简单的调用方式:

uni.openDocument({ filePath: '/static/hello.pdf', success: () => { console.log('打开文档成功') }, fail: (err) => { console.log('打开文档失败', err.errCode, err.errMsg) } })

OpenDocumentSuccess被定义为空对象类型(interface.uts 第 4 行),即打开动作本身没有业务返回值,成功与否由回调与错误码体现。

三、错误码规范:1300601~1300604 的完整语义

插件定义了统一错误码枚举OpenDocumentErrorCode(interface.uts 第 8-280 行),错误文案的集中映射位于 utssdk/unierror.uts:

| 错误码 | 枚举注释 | 源码中的 errMsg | 触发场景 | | -- | -- | -- | -- | |1300601| 路径无效 |Invalid file path| 传入路径无法被识别为有效本地文件(如 iOS 端非 fileURL 的路径) | |1300602| 文件不存在 |File not exist| 本地文件校验失败(文件不存在或读取失败) | |1300603| 不支持该文件类型 |Not support this filetype| 无法解析 MIME/类型,或系统无应用可处理(AndroidstartActivity抛异常);鸿蒙端显式抛出 | |1300604| 其他未知错误 |Unkowned error| 兜底错误(如 Android 端获取不到 Activity、文件复制失败) |

OpenDocumentErrorImpl(unierror.uts 第 15-25 行)继承UniError,并统一设置errSubject = 'uni-openDocument',错误消息从映射表中按错误码取出,未命中时为空字符串:

export class OpenDocumentErrorImpl extends UniError implements IOpenDocumentError { constructor(code: OpenDocumentErrorCode) { super(); this.errSubject = OpenDocumentUniErrorSubject; // 'uni-openDocument' this.errCode = code; this.errMsg = OpenDocumentUniErrors[code] ?? ''; } }

各平台实现统一通过该错误类构造fail/complete回调的入参,保证跨端错误语义一致。官方示例页(见下文)在fail中直接读取err.errCode并以 Toast 展示,即此错误码的典型消费方式。

四、实战示例:下载远程文档后打开

仓库自带的官方示例页 src/pages/API/open-document/open-document.uvue 给出了完整可运行的范式:对网络文档先用uni.downloadFile下载为本地临时文件,再调用uni.openDocument;对本地静态资源则直接传入路径:

const openDocument = (item: FileItem) => { if (item.url.startsWith('http')) { uni.showLoading({ title: '下载中', mask: true }) uni.downloadFile({ url: item.url, success: (res) => { uni.openDocument({ filePath: res.tempFilePath, // 下载后的本地临时文件 success: () => { uni.hideLoading() }, fail: (err) => { uni.hideLoading() uni.showToast({ title: '错误码:' + err.errCode.toString(), icon: "error" }) } }) }, fail: (err) => { /* 下载失败处理 */ } }) } else { uni.openDocument({ filePath: item.url, // 静态资源路径,如 '/static/test-image/logo.svg' success: () => { console.log('打开文档成功') }, fail: (err) => { /* 错误码展示 */ } }) } }

示例页覆盖了pdf/doc/docx/ppt/pptx/xls/xlsx/zip/br/mp3/mp4/svg等十余种文件类型的打开,展示了 App 端“原则上可以打开任意文件”的能力边界。仓库另一示例页 src/pages/API/get-file-system-manager/filemanage.uvue 中也能看到从文件系统管理器获取路径后直接交给uni.openDocument的调用方式,说明filePathuni.getFileSystemManager()的本地路径体系天然兼容。

五、各平台底层实现原理(源码级解析)

5.1 Android:ACTION_VIEW + FileProvider + MimeTypeMap

Android 实现位于 utssdk/app-android/index.uts,核心流程:

  1. 路径归一化:通过UTSAndroid.convert2AbsFullPath将相对路径转为绝对路径;
  2. 文件有效性校验isValidFile,第 75-128 行):区分三类路径——
    • /android_asset前缀:通过activity.getAssets().open(...)校验存在性,若有效则把文件复制到外部缓存目录getExternalCacheDir()/uni-document/下(使用依赖的uni.getFileSystemManager().copyFileSync),因为 FileProvider 无法直接暴露 assets 资源;
    • content://前缀:通过getContentResolver().openInputStream校验可读;
    • 其余普通路径:new File(path).exists()校验;
  3. 构造 URI(第 38-47 行):content://路径直接解析;Android 7.0(API 24,Build.VERSION_CODES.N)及以上使用FileProvider.getUriForFile(activity, packageName + '.dc.fileprovider', file)生成可共享的文件 URI,以下走兼容分支;
  4. 构造 Intent(第 48-57 行):ACTION_VIEW+FLAG_ACTIVITY_NEW_TASK+FLAG_GRANT_READ_URI_PERMISSION;若显式传了fileType,则用MimeTypeMap.getSingleton().getMimeTypeFromExtension(...)解析 MIME 并调用setDataAndType,否则只setData
  5. 拉起与兜底activity.startActivity(intent)成功则触发success;抛出Exception说明系统无应用可处理该类型,触发错误码1300603;拿不到UniActivity时触发1300604

此外,utssdk/app-android/config.json 声明了hooksClass: "uts.sdk.modules.DCloudUniOpenDocument.UniOpenDocumentHookProxy",对应源码第 14-28 行的UniOpenDocumentHookProxy:在应用onCreate时异步清空uni-document/缓存目录中的遗留文件,避免 assets 复制产物无限累积。

5.2 iOS:QuickLook 原生预览控制器

iOS 实现位于 utssdk/app-ios/index.uts,直接使用系统框架QuickLookQLPreviewController提供文档预览:

  • openDocument委托给单例DocumentPreviewer.shared.presentDocument(options)(第 14-18 行);
  • presentDocument(第 33-69 行)将filePathUTSiOS.convert2AbsFullPath归一化后构造URLtmpUrl.isFileURL == false1300601(路径无效),FileManager.default.fileExists为 false 报1300602(文件不存在);
  • 通过UTSiOS.getCurrentViewController().present(...)无动画方式弹出QLPreviewController,并实现dataSource/delegate回调(第 71-87 行)提供预览项UniPreviewItempreviewControllerDidDismiss中还处理了关闭后立即重新呈现的边界场景。

注意 iOS 端对http(s)开头的路径不做绝对路径转换(第 36-38 行),说明该端的设计预期仍是传入本地文件。

5.3 鸿蒙:Want 拉起 + uniformTypeDescriptor 类型映射

鸿蒙实现位于 utssdk/app-harmony/index.uts,走系统startAbility拉起文档查看应用:

  • 类型解析getContentType,第 19-28 行):优先取fileType,否则从filePath取扩展名,经uniformTypeDescriptor.getUniformDataTypeByFilenameExtensiongetTypeDescriptor得到首个mimeTypes[0];解析失败或传入了不支持的fileType时显式抛错码1300603
  • 路径与存在性校验(第 50-62 行):UTSHarmony.convert2AbsFullPath归一化后,以/开头的绝对路径用fs.statSync校验存在,否则抛1300602
  • 资源目录文件特殊处理(第 64-81 行):若文件位于getContext().resourceDir下(即打包进应用的只读资源),先通过fs.open/copyFile复制到临时目录TEMP_PATH/openDocumentCache再打开,因为只读资源目录无法直接授权给其他应用;
  • 拉起应用(第 82-91 行):fileUri.getUriFromPath生成 URI,构造Wantaction: 'ohos.want.action.viewData'flags携带读写与持久化授权位、uritype一并传入abilityContext.startAbility(want)
  • 异步封装(第 94-103 行):通过defineAsyncApi<OpenDocumentOptions, OpenDocumentSuccess>(API_OPEN_DOCUMENT, ...)注册,_openDocument成功resolve({}),失败以错误码(ErrorWithCode携带的错误码,未知错误兜底1300604reject,并向下兼容导出IOpenDocumentError、各回调类型等全部类型。

源码第 87 行的注释还记录了一个工程经验:传入type反而可能减少可被调起的应用数量(如 zip 场景),说明鸿蒙端在未指定类型时的“尽力打开”策略是刻意的取舍。

六、UTS 语言与 UTS 插件机制(原文档核心内容)

6.1 uts:可编译为多端原生语言的跨端语言

按插件 readme.md 的定义,uts(uni type script)是一门跨平台、高性能、强类型的现代编程语言,可被编译为不同平台的编程语言:

| 目标平台 | 编译产物语言 | | -- | -- | | Android | Kotlin | | iOS | Swift | | 鸿蒙 OS | ArkTS | | web 平台 / 小程序 | JavaScript |

uts 采用与 TypeScript 基本一致的语法规范,支持绝大部分 ES6 API;为了跨端进行了若干约束和平台特定增补。过去在 JS 引擎下运行支持的语法,大部分在 uts 的处理下也能平滑地在 Kotlin/Swift 中使用,但存在无法抹平的差异,此时需要使用条件编译(如本插件unierror.uts第 16-18 行仅在APP-ANDROID || APP-HARMONY下声明override errCode的做法),在条件编译分支内可调用平台特有扩展语法。

6.2 UTS 插件的组织方式:utssdk 目录按平台分离

UTS 插件是一种特定形态的 uni_modules 插件,核心目的是允许 uni-app/uni-app x 开发者使用 UTS 语法调用扩展 API(封装原生系统 API 或三方 SDK)。实现代码位于utssdk目录并按平台分离,uni-openDocument 即完全遵循这一规范:

| 目录/文件 | 目标平台 | 实现语言 | 作用描述 | | -- | -- | -- | -- | |utssdk/app-android| Android | UTS, Kotlin, Java | UTS 插件在 Android 平台上的具体实现源码 | |utssdk/app-ios| iOS | UTS, Swift | UTS 插件在 iOS 平台上的具体实现源码 | |utssdk/app-harmony| HarmonyOS(鸿蒙) | UTS, ArkTS | UTS 插件在 HarmonyOS 平台上的具体实现源码 | |utssdk/*.uts| 多平台共用 | UTS | 使用 UTS 编写、可供所有平台共用的实现源码(如本插件的interface.utsunierror.uts) |

对照本插件的源码可以直观看到:interface.uts定义纯类型与接口(跨端共用),protocol.uts定义 API 常量,unierror.uts定义错误模型,而真正的平台行为实现分别在三个app-*目录中通过export const openDocument: OpenDocument = ...提供——编译器会按目标平台选择对应的实现注入uni.openDocument

七、平台支持范围与使用注意事项

综合 package.json 的platforms声明与 interface.uts 中逐参数标注的@uniPlatform能力注释:

  • App 端(Android/iOS):为插件的主要实现目标(Kotlin/Swift 实现标记为y),参数注释中 Android/iOS 标注unixVer: 4.71起可用;
  • 鸿蒙端:标注uniVer: 4.31unixVer: 4.61、vapor 版本 5.0 起可用;
  • 微信小程序:宿主与 uni-app 均标记为支持(hostVer: √unixVer: 4.41),但fileType仅限doc, xls, ppt, pdf, docx, xlsx, pptx七种;
  • 百度、QQ、快手小程序:宿主能力标记为可用,但 unix 侧标记为不支持(unixVer: x),说明这些宿主是否可打开取决于宿主 API 能力;
  • Web、支付宝、字节、飞书、京东小程序等:标记为不支持(x),调用不会生效,应在这些平台做好条件编译降级处理。

使用时的关键约束(均可在上述源码与注释中印证):

  1. filePath仅支持本地路径:远程文档必须先经uni.downloadFile落盘(如示例页做法),不可直接传入 http URL;
  2. 类型与 MIME 的关系:App 端传fileType会被映射为 MIME(Android 用MimeTypeMap、鸿蒙用uniformTypeDescriptor),不传则由系统按文件本身判定;微信端则必须在其白名单类型内;
  3. Android 依赖 FileProvidercontent://与 7.0+ 的 FileProvider 授权是 Android 实现的核心机制,工程需具备对应dc.fileprovider配置(由 HBuilder 运行基座自动注入);
  4. iOS 预览为应用内 QuickLook 面板:打开后由用户手动关闭返回,success在呈现调用成功后即触发;
  5. 鸿蒙资源目录文件会被复制到临时目录:因此打开后如需长期保留请自行管理拷贝。

八、总结

uni-openDocument 是理解「UTS 插件如何封装原生能力」的一个极佳样本:一份interface.uts类型契约 + 三份平台实现(Android 的 Intent/FileProvider、iOS 的 QuickLook、鸿蒙的 Want),配合统一错误码1300601~1300604,把“打开本地文档”这一系统级能力在 uni-app/uni-app x 中收敛成一个跨端一致的uni.openDocument调用。开发者既可直接使用官方示例页 open-document.uvue 的下载-打开流程落地业务,也可参照本插件的目录结构与实现方式编写自己的 UTS 扩展 API;进一步的 API 规范细节可查阅仓库文档 docs/api/open-document.md,uts 语言与 UTS 插件开发的体系化说明可参考仓库内 docs/uts 与 docs/plugin 目录下的对应文档。

【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询