uni-app x uni.openDocument 打开文档 API 完全指南:参数、错误码与跨端底层实现

uni-app x uni.openDocument 打开文档 API 完全指南:参数、错误码与跨端底层实现

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

uni.openDocument 是 uni-app x 提供的"打开文档"API,用于在 App 端和微信小程序端唤起系统/宿主能力浏览 PDF、Word、Excel、PPT 等本地文件。本文以仓库中的官方规范文档 docs/api/open-document.md 为主线,结合 uni-openDocument UTS 插件 的 Android、iOS、HarmonyOS 三端源码实现,完整讲解调用参数、失败错误码语义以及各平台底层打开文件的真实机制,帮助开发者在实际项目中正确接入并能自主排查问题。

功能定位与适用场景

uni.openDocument(options?) 的作用是打开一个本地文档文件,官方规范文档中的定位描述只有一句话:"打开文档",但其背后承载的是移动端最常见的"附件预览"诉求,典型应用场景包括:

需要特别注意的是:该 API 不支持 Web 端。官方示例页(src/pages/API/open-document/open-document.uvue)中明确提示"该 API 不支持 Web,请运行 hello uni-app x 到 App 平台体验",其页面模板中所有内容也都用 <!-- #ifdef APP --> 条件编译包裹。

平台兼容性一览

根据规范文档中的兼容性表格,uni.openDocument 的支持情况如下:

| Web | 微信小程序 | Android | iOS | HarmonyOS | | :- | :- | :- | :- | :- | | x(不支持) | 4.41 | 4.71 | 4.71 | 4.61 |

其中数字代表 uni-app x(unix 版本)的最低支持版本。从 interface.uts 中大量 @uniPlatform 注释可以印证更细粒度的版本要求:

  • App-Android / App-iOS:unixVer 4.71 起支持;iOS 同时标注 unixUtsPlugin: 4.71(即作为 UTS 插件被引入);
  • App-Harmony:unixVer 4.61 起支持,unixVaporVer: 5.0
  • 微信小程序:unixVer 4.41 起支持,宿主版本为 (跟随微信宿主能力,showMenu 参数额外要求基础库 2.11.0+);
  • 支付宝、百度、抖音、飞书、QQ、快手、京东等小程序:hostVer/uniVer/unixVer 均为 x,即不支持;
  • Web:全部为 x,不支持。

从插件声明文件 package.json 也能看到,其 uni-ext-apiapp 平台分别声明了 kotlin: trueswift: truearkts: true 三端原生实现,而 js: false,即该 API 在 App 端由原生代码实现,不经过 JS 引擎。

参数详解:OpenDocumentOptions

uni.openDocument 的参数类型为 OpenDocumentOptions,各字段定义在 interface.uts 中。完整字段如下:

| 名称 | 类型 | 必填 | 兼容性 | 描述 | | :- | :- | :- | :-: | :- | | options | OpenDocumentOptions | 否 | Web: x | uni.openDocument 参数定义 | | filePath | string | 是 | 全端 | 文件路径,仅支持本地路径 | | fileType | string | 否 | 全端 | 文件类型(扩展名),可指定文件类型打开 | | success | (res: OpenDocumentSuccess) => void | 否 | 全端 | 成功回调 | | fail | (res: OpenDocumentFail) => void | 否 | 全端 | 失败回调 | | complete | (res: any) => void | 否 | 全端 | 完成回调(成功、失败都会执行) | | showMenu | boolean | 否 | 仅微信小程序 | 是否显示右上角菜单,需基础库 2.11.0+ |

filePath:仅支持本地路径

filePath 是唯一必填参数,只接受本地路径。参考三端实现源码可确认其支持的路径形态:

  • 普通本地绝对/相对路径:Android 端通过 UTSAndroid.convert2AbsFullPath 转换为绝对路径后校验文件是否存在(app-android/index.uts);iOS 端通过 UTSiOS.convert2AbsFullPath 转换后调用 FileManager.default.fileExists 检查(app-ios/index.uts);HarmonyOS 端同样经 UTSHarmony.convert2AbsFullPath 转换并用 fs.statSync 判断存在性(app-harmony/index.uts);
  • content:// 开头的 URI(仅 Android):源码中 path.startsWith('content://') 会直接 Uri.parse 并使用 ContentResolver.openInputStream 校验可读性;
  • /android_asset/ 开头的应用内置资源(仅 Android):源码 isValidFile 会通过 getAssets().open 读取,并自动将其复制到应用外部缓存目录 .../uni-document/ 下再打开——这解释了"仅支持本地路径"的同时仍能打开工程内置静态文件的原因。

因此不传 http(s) 网络地址直接调用:文档示例代码中凡是 http 开头的 URL,都会先走 uni.downloadFile 下载,再以 res.tempFilePath 作为 filePath 传入。

fileType:文件类型(扩展名)

fileType 用于显式指定文件类型,注意两端的语义差异

  • 微信小程序端:仅支持 doc, xls, ppt, pdf, docx, xlsx, pptx 这 7 种类型,超出范围将无法打开;
  • App 端(Android/iOS/HarmonyOS):由系统根据文件内容决定由哪个应用打开,原则上可以打开任意文件。参数中的扩展名仅作为辅助信息用于推导 MIME 类型:
    • Android:MimeTypeMap.getSingleton().getMimeTypeFromExtension(options.fileType) 推导 MIME 类型并 intent.setDataAndType(uri, mimeType)app-android/index.uts);
    • HarmonyOS:uniformTypeDescriptor.getUniformDataTypeByFilenameExtension 推导类型描述符并取出首个 MIME 类型,若传入的 fileType 无法识别会抛出错误码 1300603app-harmony/index.uts)。

源码中该字段类型为 OpenDocumentSupportedTypes,即 string,且允许传 nullfileType ?: OpenDocumentSupportedTypes | null)。

三个回调与 showMenu

  • successfailcomplete 与 uni-app 其他 API 的约定一致:成功只走 success + complete,失败只走 fail + complete。从源码看,OpenDocumentSuccess 是一个空类型 {}interface.uts),成功回调不带业务数据;
  • showMenu 仅微信小程序支持(Web: x; 微信小程序: 4.41),控制打开文档页面右上角是否显示菜单,需要微信基础库 2.11.0

错误处理与 errCode 错误码

打开文档属于高风险调用(路径可能无效、文件可能被删、设备可能没有可处理该类型的应用),官方规范为失败回调定义了完整错误对象结构。

OpenDocumentFail 的结构

失败回调收到的 OpenDocumentFail 对象(对应源码中的 IOpenDocumentError,继承自 IUniError,参见 err-spec.md)包含以下字段:

| 名称 | 类型 | 必填 | 描述 | | :- | :- | :- | :- | | errCode | number | 是 | 错误码 | | errSubject | string | 是 | 统一错误主题(模块)名称 | | data | any | 否 | 错误信息中包含的数据 | | cause | Error | 否 | 源错误信息,可包含多个错误 | | errMsg | string | 是 | 错误信息 |

errCode 错误码速查

| 合法值 | 兼容性 | 描述 | 触发时机(依据源码) | | :- | :-: | :- | :- | | 1300601 | 全端 | 路径无效 | iOS 端路径转换后 isFileURL == false 时抛出(app-ios/index.uts) | | 1300602 | 全端 | 文件不存在 | Android 端 file.exists() 为 false、资源读取失败、content URI 不可读时抛出;iOS 端 fileExists 为 false 时抛出;HarmonyOS 端 fileExists 为 false 时抛出 | | 1300603 | 全端 | 不支持该文件类型 | Android 端 startActivity 抛异常(设备无应用可处理)时抛出;HarmonyOS 端 fileType 无法识别 MIME 类型时抛出 | | 1300604 | 全端 | 其他未知错误 | Android 端获取不到 Activity、资源复制异常时抛出;HarmonyOS 端兜底错误 |

这些错误码与错误消息映射定义在 unierror.uts

export const OpenDocumentUniErrors : Map<number, string> = new Map([
	[1300601, 'Invalid file path'],
	[1300602, 'File not exist'],
	[1300603, 'Not support this filetype'],
	[1300604, 'Unkowned error']
]);

errSubject 与 errMsg

源码中错误主题(模块名)固定为 'uni-openDocument'OpenDocumentUniErrorSubject),errMsg 默认取上述映射表中对应错误码的英文描述,用于统一错误上报与排查(unierror.uts)。

实战建议:在 fail 回调中不要只弹 errMsg,应把 errCode 一并展示(官方示例正是用 '错误码:' + err.errCode.toString() 弹 Toast),因为同一错误码在不同平台的触发路径不同,开发阶段能更快定位问题归属。

完整示例:从网络下载到打开文档

仓库中的官方示例页位于 src/pages/API/open-document/open-document.uvue,并在 src/pages.json 中注册为标题"uni.openDocument | 打开文档"。核心逻辑如下:

<template>
    <page-head :title="title"></page-head>
    <!-- #ifdef APP -->
    <scroll-view direction="vertical" style="flex:1">
    <!-- #endif -->
      <view class="uni-common-mt">
        <button v-for="(item, index) in fileList" :key="index" @click="openDocument(item)" style="margin: 10px;">
          打开 {{item.type}} 文件
        </button>
      </view>
    <!-- #ifdef APP -->
    </scroll-view>
    <!-- #endif -->
</template>

<script setup lang="uts">
  type FileItem = {
    type : string,
    url : string
  }

  const title = 'openDocument'
  const fileList = ref<Array<FileItem>>([
    { type: 'pdf',  url: 'https://web-assets.dcloud.net.cn/unidoc/zh/helloworld.pdf' },
    { type: 'doc',  url: 'https://web-ext-storage.dcloud.net.cn/uni-app-x/file/helloworld.doc' },
    { type: 'docx', url: 'https://web-ext-storage.dcloud.net.cn/uni-app-x/file/helloworld.docx' },
    { type: 'ppt',  url: 'https://web-ext-storage.dcloud.net.cn/uni-app-x/file/helloworld.ppt' },
    { type: 'pptx', url: 'https://web-ext-storage.dcloud.net.cn/uni-app-x/file/helloworld.pptx' },
    { type: 'xls',  url: 'https://web-ext-storage.dcloud.net.cn/uni-app-x/file/helloworld.xls' },
    { type: 'xlsx', url: 'https://web-ext-storage.dcloud.net.cn/uni-app-x/file/helloworld.xlsx' },
    { type: 'zip',  url: 'https://web-ext-storage.dcloud.net.cn/uni-app-x/file/to.zip' },
    { type: 'br',   url: '/static/filemanager/1.txt.br' },
    { type: 'mp3',  url: '/static/test-audio/ForElise.mp3' },
    { type: 'mp4',  url: '/static/test-video/10second-demo.mp4' },
    { type: 'svg',  url: '/static/test-image/logo.svg' }
  ])

  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()
              console.log('打开文档成功')
            },
            fail: (err) => {
              uni.hideLoading()
              console.log('打开文档失败', err)
              uni.showToast({ title: '错误码:' + err.errCode.toString(), icon: "error" })
            }
          })
        },
        fail: (err) => {
          uni.hideLoading()
          console.log('下载失败', err)
          uni.showToast({ title: '下载失败:' + err.errCode.toString(), icon: "error" })
        }
      })
    } else {
      uni.openDocument({
        filePath: item.url,
        success: () => {
          console.log('打开文档成功')
        },
        fail: (err) => {
          console.log('打开文档失败', err)
          uni.showToast({ title: '错误码:' + err.errCode.toString(), icon: "error" })
        }
      })
    }
  }
</script>

该示例覆盖了两类输入:

  1. 网络 URL:先 uni.showLoading 提示下载中,uni.downloadFile 下载成功后在 success 回调中把 res.tempFilePath 作为 filePath 调用 uni.openDocument;下载失败同样弹 Toast 提示;
  2. 本地路径(如 /static/... 工程内置资源):直接调用 uni.openDocument

两种分支都完整处理了 success / fail,并在失败时展示错误码——这套模板可直接复制到实际业务中,只需替换文件列表来源(例如改为从 uni.getFileSystemManager 写入的本地文件路径)。

源码剖析:三端底层实现原理

uni.openDocument 在 App 端对应 uni_modules 插件 uni-openDocument,实现代码位于 utssdk 目录并按平台拆分(package.json 中声明其依赖 uni-fileSystemManager)。各端打开文件的机制差异很大,理解后有助于排查"打不开""没反应"类问题。

Android:ACTION_VIEW + FileProvider

Android 实现(app-android/index.uts)的核心链路:

  1. 取当前 Activity,通过 UTSAndroid.convert2AbsFullPath 规范化路径;
  2. isValidFile 做三重校验:普通文件校验 File.exists()/android_asset/ 资源先经 Assets 读取并复制到外部缓存目录 getExternalCacheDir()/uni-document/content:// URI 用 ContentResolver.openInputStream 验证;
  3. Android 7.0(API 24)及以上使用 FileProvider.getUriForFile(activity, packageName + '.dc.fileprovider', file) 生成 content:// URI 并授予读权限(FLAG_GRANT_READ_URI_PERMISSION),这是 Android N 之后"FileUriExposedException"问题的标准解法;
  4. 构造 Intent.ACTION_VIEW,有 fileType 时用 MimeTypeMap 推导 MIME 类型 setDataAndType,否则直接 setData
  5. startActivity 若抛异常(说明设备上没有能打开该类型文件的应用),则回调错误 1300603
  6. 插件还在 UniOpenDocumentHookProxy.onCreate 中异步清理缓存目录残留文件,避免下载/复制的临时文档堆积(app-android/index.uts)。

iOS:QuickLook 预览控制器

iOS 实现(app-ios/index.uts)没有走"跳转第三方应用"的方案,而是在 App 内部以 QuickLook 框架的 QLPreviewController 展示文档

  1. filePathUTSiOS.convert2AbsFullPath 转为绝对路径并构造 URL
  2. 非文件 URL(isFileURL == false)报 1300601,文件不存在报 1300602
  3. 以单例 DocumentPreviewer 作为 dataSource / delegate,通过 UTSiOS.getCurrentViewController().present(...) 模态弹出 QLPreviewController
  4. UniPreviewItem 实现 QLPreviewItem 协议提供预览 URL,numberOfPreviewItems 返回 1 表示单文件预览;
  5. 预览关闭后(previewControllerDidDismiss)延迟 0.01 秒重新 present,以兼容 iOS 13+ 的 modal 呈现变化。

由于 QuickLook 原生支持 PDF、Office 文档等常见格式,iOS 端"原则上可打开任意文件"即指 QuickLook 能力范围内的格式。

HarmonyOS:Want 隐式意图 + uniformTypeDescriptor

HarmonyOS 实现(app-harmony/index.uts)通过系统 Want 隐式启动查看器:

  1. UTSHarmony.convert2AbsFullPath 转绝对路径,fs.statSync 校验存在性,否则报 1300602
  2. 若目标在 resourceDir(应用资源目录)内,先复制到临时目录 TEMP_PATH/openDocumentCache 下再打开,保证资源只读目录可被外部读取;
  3. uniformTypeDescriptor.getUniformDataTypeByFilenameExtension + getTypeDescriptor 从扩展名推导 MIME 类型,识别失败报 1300603
  4. 构造 Wantaction: 'ohos.want.action.viewData',携带 urifileUri.getUriFromPath 生成)、type(MIME 类型)以及读写持久化权限 flags,最后 startAbility 拉起系统文档查看器或第三方应用。

源码注释中还记录了一个工程细节:传入 type 参数时只调起 2 个解压缩应用,不传反而能调起 4 个——说明某些类型显式传 fileType 反而会收窄可选应用范围,实际业务中可权衡是否传该参数。

统一错误对象

三端在失败时均构造 OpenDocumentErrorImpl(继承 UniError,见 err-spec.md),统一设置 errSubject = 'uni-openDocument'errCodeerrMsgunierror.uts),因此在业务代码中无需关心平台差异,只需按错误码处理。

使用要点与常见问题

  1. Web 端不可用:页面若需同时支持 H5 与 App,应对 uni.openDocument 做条件编译或能力判断(示例页全程 #ifdef APP 包裹),避免 H5 上报错;
  2. filePath 必须本地化:网络文件先 uni.downloadFile/static/ 内置资源在 Android 端会被自动复制到缓存目录(getExternalCacheDir()/uni-document/),iOS/HarmonyOS 同样有各自的临时目录处理,业务侧无需关心细节,但要注意缓存可能被系统清理;
  3. fileType 语义差异:微信小程序仅 7 种类型(doc/xls/ppt/pdf/docx/xlsx/pptx);App 端传错扩展名在 HarmonyOS 上会触发 1300603,Android 上传了无法识别的类型时 setDataAndType 的 MIME 为 null,可能影响应用筛选,不必要时可不传;
  4. 1300603 的另一层含义(Android)startActivity 抛异常说明设备上没有能打开该文件的应用,这与"类型不受支持"是同一错误码,需要向用户提示"未安装可打开该文件的应用";
  5. 失败诊断顺序:先看 errCode——1300601 查路径格式、1300602 查文件是否存在、1300603 查类型与系统应用、1300604 查平台异常(如 Android 获取 Activity 失败);再看 errMsg/errSubject 定位模块;
  6. 完整可运行示例:官方演示工程中的 open-document.uvue 覆盖了从下载到打开的完整闭环,且示例文件在仓库 src/static/ 下均有对应资源(如 filemanager/1.txt.brtest-audio/test-video/test-image/),可直接运行于 App 端验证多类型文件打开效果。

通过本文的参数表、错误码对照与三端源码解读,开发者可以准确地在 uni-app x 项目中集成"打开文档"能力,并在出现问题时依据 errCode 快速定位是路径、文件、类型还是系统环境问题。

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

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

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值