Skip to content

媒体与离线 API

1. 推荐的媒体发送接口

api-id用途必要元数据返回
media.send_image发送图片本地文件、MIME、宽、高Apple XHIMMediaSendResult;其他端 XHIMMediaTask
media.send_video发送视频本地文件、MIME、时长、宽、高Apple XHIMMediaSendResult;其他端 XHIMMediaTask
media.send_audio发送音频本地文件、MIME、时长、可选 waveformApple XHIMMediaSendResult;其他端 XHIMMediaTask
media.send_file发送文件本地文件、MIME、展示文件名Apple XHIMMediaSendResult;其他端 XHIMMediaTask

这些便利接口先计算内容哈希,再把上传任务和依赖消息意图一起持久化给 Core。 成功返回表示“任务已可靠接受”,随后即使 App 重启,Core 仍可按策略继续分片、 审核、提交对象和入队消息。

聊天 UI 可以在可靠受理之前先显示一个仅存在于宿主内存的乐观视频气泡:先读取 首帧、时长和宽高,并预生成 clientMessageID;文件接管、哈希和上传放到后台, 最终以相同 ID 的耐久消息替换占位。不要把占位写成另一条文字 [视频] 消息。

平台入口:

  • iOS:client.sendImageMessage/sendVideoMessage/sendAudioMessage/sendFileMessage
  • macOS:client.sendImageMessage/sendVideoMessage/sendAudioMessage/sendFileMessage
  • Android:client.sendImage/sendVideo/sendAudio/sendFileFile 重载;
  • Windows:XHIMMediaMessaging.SendImageAsync/...
  • HarmonyOS:XHIMMediaMessaging.sendImage/...

Apple 返回 .durable(XHIMMediaTask) 时表示 Core 持久化受理;返回 .sent(XHIMSendReceipt) 时表示 SDK Facade 已完成服务端授权上传并把媒体消息 交给可靠 Outbox。两种模式都由 SDK 管理 Credential、哈希、Prepare、上传、 Complete 和消息组装。直接传入 uploader 的重载只用于自定义 Product Adapter; 普通接入不要自行上传到临时 URL 再组装消息。

服务端本地对象存储使用可续传 PATCH + Content-Range + Upload-Offset,S3 兼容存储通常使用签名 PUT。Apple Facade 同时支持两种协议;业务代码不能把 授权方法硬编码为 PUT

2. durable media task

api-id用途主要输入返回
media.accept_upload接受一个完整上传意图XHIMMediaUploadIntentXHIMMediaTask
media.accept_download接受一个下载/缓存意图XHIMMediaDownloadIntentXHIMMediaTask
media.get_task查询任务快照taskIDXHIMMediaTask
media.cancel_task取消持久化媒体工作流taskIDXHIMMediaTask

cancelMediaTask 和“取消等待某次 API”不是一回事:

  • 取消 Swift Task / Coroutine / CancellationToken / requestId,只停止当前调用;
  • cancelMediaTask(taskID) 会改变持久化业务任务状态,并阻止依赖消息继续提交。

XHIMMediaTask.state

text
pending → authorizing → transferring → verifying → completed
                         ↕ paused           └──────→ failed
任意未终态 ────────────────────────────────→ cancelled

未知 native state 必须保留 task ID 并稍后重查,不能强行映射为 failed。 messageEnqueued == true 表示媒体引用已经进入依赖消息;最终消息投递状态仍从消息 查询判断。

3. 媒体缓存读取

Apple 普通业务页面优先使用收到即下载的便利接口:

swift
let content = try XHIMMediaMessageContent.decode(message: message)
let downloaded = try await client.downloadMediaMessage(
    message,
    cacheDirectoryURL: mediaCacheDirectory
)
preview(downloaded.localFileURL, content: content)

它支持图片、视频、语音和文件消息,内部完成账号下载授权、禁止重定向、大小和 SHA-256 校验、临时文件清理以及缓存命中。只有自定义 Product Adapter、后台 持久任务或需要流式随机读的页面才使用下面的低层任务/Reader。

聊天 UI 收到 .audio 后,应把已校验的 localFileURL 交给页面级单实例 AVAudioPlayer,点击气泡切换播放/暂停,切换消息时停止上一条;不要把语音发送 到 Quick Look 或单独的播放页面。播放状态必须按消息 ID 管理,不能保存在可复用 Cell 内。页面退出、开始录音、App 退到后台或音频中断时应停止播放并释放 AVAudioSession。这只是 UI 消费方式,不改变 SDK 的媒体下载合同。

fallbackText 只属于通知、会话摘要和未知类型降级。聊天详情页必须按 contentType + contentVersion 使用 XHIMMediaMessageContent.decode,再选择 图片、视频、语音或文件 Renderer;不要先判断 fallbackText 是否为空,否则 有效媒体会错误显示成 [图片][视频] 等普通文字。位置、名片分别使用 XHIMLocationContentXHIMContactCardContent 解码。解码失败应显示不支持或 内容损坏卡片,不能吞掉错误后伪装成文本消息。

media.open_cache

输入 cacheKey,返回 XHIMMediaCacheReader。Reader 公开:

api-id / 操作iOSmacOSAndroidWindowsHarmonyOS
大小byteSizebyteSizebyteSizeByteSizebyteSize
media.cache_read 读取范围read(offset:length:)read(offset:length:)read(offset,length)ReadAsync(offset,length)read(offset,length)
media.cache_chunks 分块消费chunks(...)chunks(...)chunks(...)ReadChunksAsync(...)stream(...)
media.cache_close 关闭close()close()close()DisposeAsync()close()

Reader 读取 SDK 管理的缓存对象,不暴露数据库路径、对象存储密钥或临时下载 凭证。读取结束后必须显式关闭。UI 播放器需要随机读时保持一个 Reader,不要每帧 重新打开。

4. 媒体消息模型

XHIMMediaMessageFactory 提供:

api-id工厂
media.factory_imageimage / Windows Image
media.factory_audioaudio / Windows Audio
media.factory_videovideo / Windows Video
media.factory_filefile / Windows File

生成的仍是 XHIMOutgoingMessage,可直接交给 sendMessage。媒体 payload 只包含 XHIMMediaRef 和展示元数据:

  • mediaID
  • contentHashSHA256
  • byteSize
  • mimeType
  • storageRevision
  • 可选 XHIMMediaEncryption

永远不要把本地文件路径、系统相册 URI、管理员密钥或即将过期的 signed URL 写入消息 payload。

5. 系统 Picker 的职责

SDK UI Kit 提供相机、相册、录音和文件入口;相册入口可以同时选择图片和视频。 系统权限及 Picker 仍属于 宿主 App:

  1. 用户在宿主页面触发 Picker;
  2. 宿主取得有权限的本地文件/URI;
  3. 调用媒体便利接口;
  4. 立即释放临时系统授权,长期任务由 SDK Core 接管;
  5. 订阅 mediaTaskUpdated 更新进度和失败态。

购买方可以替换 Picker 与 UI,不需要修改 Core。

iOS 商业 Demo 使用 30 秒拍摄上限;直接可读的 AVURLAsset 不做中等质量 二次转码,只有组合资源才使用 passthrough 导出。列表渲染应按图片、视频、 语音、位置和通用卡片隔离复用池,并在复用时取消旧下载、停用旧尺寸约束和清空 缩略图,避免滚动或输入面板动画时出现压扁气泡。

6. 离线只读 Reader

当后台扩展、通知扩展、桌面快速搜索或受限进程不允许启动网络 Client 时,使用 XHIMOfflineReader。它只打开同一账号的加密本地投影,不启动登录、WebSocket、 同步或 Outbox。

api-id用途输入返回
offline.open打开只读数据库storage、App ID、数据库 Key ProviderXHIMOfflineReader
offline.messages会话消息分页conversation、cursor、limitXHIMMessagePage
offline.search_messages本地消息搜索query、cursor、limitXHIMMessagePage
offline.conversations会话分页cursor、limitXHIMConversationPage
offline.friend_requests好友申请分页cursor、limitXHIMSocialPage
offline.friendships好友分页cursor、limitXHIMSocialPage
offline.groups群组分页cursor、limitXHIMSocialPage
offline.group_members群成员分页conversation、cursor、limitXHIMSocialPage
offline.blocks黑名单分页cursor、limitXHIMSocialPage
offline.group_join_requests入群申请分页cursor、limitXHIMSocialPage
offline.close关闭 ReaderVoid

离线 Reader 与在线 Client 共享模型,但不保证读取瞬间包含刚到达服务器的数据。 需要最新状态时回到主 App,由在线 Client 完成同步后再查。

7. 数据库 Key

Reader 的 Key Provider 必须从平台安全存储读取当前账号数据库 Key:

  • iOS Keychain;
  • macOS Keychain;
  • Android Keystore 包装后的 Key;
  • Windows DPAPI / Credential Locker;
  • HarmonyOS HUKS。

不要把 Key 写进源码、UserDefaults、SharedPreferences、注册表明文或普通配置 文件。Key 错误时 Reader 应失败关闭,不能退回明文 SQLite。

XHIM 客户端 SDK 与服务端文档