资讯动态

Expo expo-sharing 模块详解:文件分享与接收共享内容的完整实现指南

发布时间:2026/9/10 13:51:15 来源:尧图企业网站定制
Expo expo-sharing 模块详解文件分享与接收共享内容的完整实现指南【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expoexpo-sharing是 Expo 官方 SDK 中负责应用间数据交换的模块一方面提供shareAsync将本地文件直接分享给其他兼容应用另一方面通过 Share into 机制接收其他应用共享进来的文本、URL、图片、视频和文件。读完本文你将掌握该模块在 Managed 与裸 React Native 项目中的安装配置方法、全部 JavaScript API 及选项参数的平台差异以及基于 Android Intent 与 iOS Share Extension 的底层实现原理能够在应用中完整落地分享文件出去和接收分享内容两条链路。模块定位双向的应用间数据交换按照 packages/expo-sharing/README.md 的定义expo-sharing 提供两类核心能力分享出去Share out通过系统分享面板将文件直接分享给能处理该文件类型的其他应用接收进来Share into接收从其他应用共享到本应用的兼容数据包括文本、URL、音频、图片、视频和通用文件。模块入口 packages/expo-sharing/src/index.ts 只导出三样东西useIncomingShareHook、Sharing.types中的全部类型定义以及Sharing中的全部 API 函数。安装与配置Managed Expo 项目在 Managed 项目中直接通过 Expo CLI 安装即可npx expo install expo-sharing裸 React Native 项目README 明确指出在裸 React Native 项目中接收共享内容Share into不被支持。要使用该模块必须先完成expo基础包的安装与配置。README 给出的操作步骤为将包加入 npm 依赖npx expo install expo-sharingAndroid 端无需额外设置No additional set up necessary。使用 Config Plugin 配置 Share Extension接收共享内容功能在两个平台都依赖系统级机制Android 依赖 Intent FilteriOS 依赖 Share Extension。从源码结构看仓库在 packages/expo-sharing/plugin 中提供了一个配置插件通过 app.plugin.js 暴露允许在app.json中声明扩展行为。插件参数类型定义见 packages/expo-sharing/plugin/src/sharingPlugin.types.tstype ShareExtensionConfigPluginProps { ios?: { enabled?: boolean; extensionBundleIdentifier?: string; appGroupId?: string; activationRule?: ActivationRule; // 支持结构化配置或字符串 }; android?: { enabled?: boolean; singleShareMimeTypes?: string[]; // 对应 android.intent.action.SEND multipleShareMimeTypes?: string[]; // 对应 android.intent.action.SEND_MULTIPLE }; };iOS 端的ActivationRule结构化配置可以精细控制应用接受哪些类型、最多接收多少条配置项默认值含义supportsTextfalse是否接受共享的纯文本supportsWebUrlWithMaxCount0不接受最多接收的 Web URL 数量supportsImageWithMaxCount0最多接收的图片数量supportsMovieWithMaxCount0最多接收的视频数量supportsFileWithMaxCount0最多接收的文件数量supportsWebPageWithMaxCount0最多接收的网页数量supportsAttachmentsWithMaxCount0最多接收的附件数量插件实现上iOS 侧会生成 ShareIntoViewController.swift 模板文件并改写 Xcode 工程见 withShareExtensionXcodeProject.ts、withAppGroupId.tsAndroid 侧则向AndroidManifest.xml注入 Intent Filter见 withAndroidIntentFilters.ts。核心 JavaScript API全部 API 实现在 packages/expo-sharing/src/Sharing.ts并通过 SharingNativeModule.ts 桥接到名为ExpoSharing的原生模块。isAvailableAsync()export async function isAvailableAsync(): Promiseboolean判断当前环境能否使用分享 API。在 Web 平台的实现SharingNativeModule.web.ts中它会检查navigator.share是否存在不存在则返回false。shareAsync(url, options)export async function shareAsync(url: string, options: SharingOptions {}): Promisevoid打开系统分享面板将本地文件共享给能处理该文件类型的应用。url必须是本地文件 URLfile://协议若原生模块不可用会抛出UnavailabilityError(Sharing, shareAsync)。Share into 系列 API// 同步返回原始共享数据无共享内容时返回空数组 export function getSharedPayloads(): SharePayload[]; // 返回已解析的共享数据包含可直接读取/展示内容的附加信息 export async function getResolvedSharedPayloadsAsync(): PromiseResolvedSharePayload[]; // 清除已共享进来的数据 export function clearSharedPayloads(): void;getResolvedSharedPayloadsAsync与getSharedPayloads的区别在于解析后的 payload 携带contentUri、contentType、contentSize等字段可直接读取内容。例如一个 Web URL 被共享进来时resolved payload 会包含该 URL 内容的相关元信息。源码注释特别提示解析过程可能需要网络连接例如带重定向的 URL 需要跟随解析。SharingOptions 选项详解SharingOptions定义在 packages/expo-sharing/src/Sharing.types.ts各字段有显著的平台差异type SharingOptions { /** 目标文件的 MIME 类型如 image/jpeg。 * Android: 设置分享 Intent 的 mimeType * iOS: 当 UTI 无法提供扩展名时决定文件类型。 * 注意: iOS 上 MIME 类型不会作为元数据附加到共享项 * 只用于给文件补上匹配的扩展名分享面板据此推断类型 * 无法映射到标准扩展名的类型如 application/octet-stream无效。 * 注意: iOS 上 mimeType 优先于文件已有扩展名原扩展名不会移除 * 而是追加——image.pdf 以 image/jpeg 分享会变为 image.pdf.jpeg。 */ mimeType?: string; /** [UTI]iOS能映射到标准扩展名时优先于 mimeType。 * iOS 只能解析系统或已安装应用注册的类型 * 无法映射时忽略 UTI 改用 mimeType。 * 映射成功时同样采用追加扩展名策略。 */ UTI?: string; /** 分享对话框标题。仅 Android 与 Web 支持。 */ dialogTitle?: string; /** iPad 上分享面板的锚点位置。仅 iOS 支持。 */ anchor?: { x?: number; y?: number; width?: number; height?: number }; };几个关键行为约束值得注意优先级iOS 上UTImimeType见 SharingModule.swift 的declaredContentType先查options.UTI再查options.mimeType。类型声明是事实来源传入的mimeType/UTI不与文件真实内容校验模块假定你声明的就是实际类型。dialogTitle在 iOS 上不生效于标题显示——但它对应UIActivityViewController的title属性主要用于辅助功能场景。Share into 数据类型体系接收共享内容的类型体系同样在 Sharing.types.ts 中定义标注为experimentalShareTypetext | url | audio | image | video | file表示共享内容的种类SharePayload原始数据核心字段为value文本消息体 / URL 字符串 / 文件 URI、shareType默认text、mimeType默认text/plainResolvedSharePayload解析后的联合类型分为UriBasedResolvedSharePayload可通过contentUri访问内容contentType为audio | file | video | image | website和TextBasedResolvedSharePayloadcontentType为text解析后新增字段contentUri处理重定向时为最终目标 URI文本类型为null、contentType、contentMimeType、originalName取自suggestedFilenameHTTP 头或contentUri末段路径、contentSize。useIncomingShare Hook响应式接收共享内容useIncomingShare 是面向接收链路的 React Hook将上述命令式 API 封装成响应式结果对象type UseIncomingShareResult { sharedPayloads: SharePayload[]; // 原始 payload同步、立即可用 resolvedSharedPayloads: ResolvedSharePayload[]; // 解析后的 payload clearSharedPayloads: () void; isResolving: boolean; // 是否正在解析 error: Error | null; // 解析错误 refreshSharePayloads: () void; // 强制刷新 };其内部机制值得细读首次挂载即拉取useState(getSharedPayloads())以惰性初始化读取当前共享数据前后台切换触发刷新监听AppState的change事件当应用回到active状态时调用refreshSharePayloads()——这正对应用户从其他 App 分享内容后切回本应用的真实场景变更检测降低网络开销sharePayloadsAreEqual以value|mimeType|shareType组合作为 key 做多重集合比较数据未变化时跳过getResolvedSharedPayloadsAsync避免重复发起网络请求源码注释Do not run getResolvedSharedDataAsync if the data hasnt changed to reduce network usage错误归一化原生抛出的非Error对象会被包装为Unknown error during shared payload resolution。Web 平台的行为边界SharingNativeModule.web.ts 给出了 Web 端的完整契约isAvailableAsync检查navigator.share即 Web Share API是否存在shareAsync底层调用navigator.share({ ...options, url })源码注释明确navigator.share仅在 HTTPS 下可用不支持时抛出UnavailabilityError(navigator, share)getSharedPayloads/getResolvedSharedPayloadsAsync/clearSharedPayloads在 Web 上直接抛错Receiving share payloads is not supported on web.也就是说Web 端只有分享出去链路可用且受 HTTPS 前提约束。Android 端实现原理Android 原生模块实现在 packages/expo-sharing/android/src/main/java/expo/modules/sharing/SharingModule.kt模块名ExpoSharing。shareAsync的关键调用链URL 校验getLocalFileFoUrl要求 scheme 必须为file否则抛出InvalidArgumentException(Only local file URLs are supported...)路径为空或无读取权限经FilePermissionService校验同样拒绝FileProvider 转换通过FileProvider.getUriForFile将本地文件转为content://URIauthority 为应用包名 .SharingFileProvider由 SharingFileProvider.kt 与 sharing_provider_paths.xml 声明——这是 Android 7.0 分享文件的安全标准做法MIME 类型推断优先取params.mimeType缺失时回退URLConnection.guessContentTypeFromName再回退*/*权限授予先queryIntentActivities查出所有能处理该 Intent 的应用包名逐一grantUriPermission(..., FLAG_GRANT_READ_URI_PERMISSION)再构建ACTION_SENDIntentEXTRA_STREAM携带 content URIsetTypeAndNormalize(mimeType)并用Intent.createChooser包装dialogTitle在此生效并发保护pendingPromise保证同一时刻只有一个分享流程重复调用抛出SharingInProgressException分享面板返回时经OnActivityResultREQUEST_CODE 8524resolve Promise。接收侧由 SimpleShareIntentDataParser.kt原始解析和 ResolvingShareIntentDataParser.kt解析元信息分别支撑getSharedPayloads与getResolvedSharedPayloadsAsync共享的 Intent 缓存在 SharingSingleton.kt 中clearSharedPayloads即把SharingSingleton.intent置空。iOS 端实现原理iOS 原生模块实现在 packages/expo-sharing/ios/SharingModule.swift核心要点UIActivityViewControllershareAsync在.main队列上运行先校验文件可读FileSystemUtilities.isReadableFile构建UIActivityViewController并从当前可见的UIViewControllerpresent拿不到当前控制器时抛出MissingCurrentViewControllerException。声明类型的暂存机制由于 iOS 分享面板依据扩展名推断类型当声明的UTI/mimeType映射出的扩展名与文件现扩展名不一致时prepareShareUrl会在应用缓存目录下的expo-sharing-tmp/sessionId中建立硬链接跨卷失败则回退为复制生成带正确扩展名的新 URL 参与分享。分享完成或应用重启后completedStagedDirectories/cleanupPreviousStagingSessions负责清理这些暂存文件。这与类型注释中image.pdf分享为image.pdf.jpeg的行为完全对应。iPad 锚点configurePopoverIfNeeded仅在 iPaduserInterfaceIdiom .pad上生效将options.anchor的x/y/width/height映射到popoverPresentationController.sourceRect默认锚定在视图底部中心。Promise 泄漏修复completionWithItemsHandler无条件promise.resolve(nil)源码注释说明旧实现只处理了 4 种(activityType, completed)组合中的 2 种用户选择活动后取消二级对话框会导致 Promise 悬挂。Share into 的 App Group 通道接收链路依赖ExpoShareIntoAppGroupIdInfo.plist 键由配置插件写入共享扩展将 payload 以 JSON 写入UserDefaults(suiteName: appGroupId)的SHARE_INTO_DEFAULTS_KEYgetSharedPayloads直接解码该字典getResolvedSharedPayloadsAsync用withThrowingTaskGroup并发解析各 payload 并按原索引回填clearSharedPayloads则移除该字典键。实际使用示例仓库内的示例应用可直接参考apps/native-component-list/src/screens/SharingScreen.tsx 展示了expo-sharing在功能演示矩阵中的用法。一个典型的分享文件代码形态为import * as Sharing from expo-sharing; import { useState } from react; function ShareButton({ fileUri }: { fileUri: string }) { const [sharing, setSharing] useState(false); return ( button disabled{sharing} onClick{async () { if (!(await Sharing.isAvailableAsync())) return; setSharing(true); try { await Sharing.shareAsync(fileUri, { dialogTitle: Share this file, mimeType: image/jpeg, // Android 生效iOS 用于补扩展名 // UTI: public.jpeg, // iOS 专用优先于 mimeType // anchor: { x, y, width, height }, // iPad 锚点 }); } finally { setSharing(false); } }} Share /button ); }接收侧则可结合 Hook 使用import { useIncomingShare } from expo-sharing; function IncomingShareView() { const { sharedPayloads, resolvedSharedPayloads, isResolving, error, clearSharedPayloads, } useIncomingShare(); if (error) return Text解析失败: {error.message}/Text; if (isResolving) return Text解析中…/Text; if (sharedPayloads.length 0) return Text暂无共享内容/Text; return ( ul {resolvedSharedPayloads.map((p) ( li key{p.contentUri ?? p.value} {p.contentType}: {p.contentUri ?? p.value} /li ))} /ul ); }小结与适用前提平台差异速览分享文件shareAsync在 Android / iOS / Web 均可用Web 需 HTTPS 且走navigator.share接收共享内容getSharedPayloads等及useIncomingShare仅 Android / iOS 支持且裸 React Native 项目不受支持类型参数mimeType/UTI在 iOS 上通过追加扩展名 缓存目录暂存文件的方式生效声明值不与文件内容校验且UTI优先于mimeType接收链路依赖系统机制Android 的 Intent Filter可用配置插件声明singleShareMimeTypes/multipleShareMimeTypesiOS 的 Share Extension App Group可用配置插件声明appGroupId与activationRule模块当前版本信息可参见 packages/expo-sharing/package.json 与 CHANGELOG.mdShare into 系列 API 在类型注释中标注为experimental使用时应留意后续版本的接口变化。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价