feat: xwebview handleDownloadRequest + README 更新
- 新增 handleDownloadRequest() 统一下载处理函数 - 导出 download 相关工具函数 - 更新 rn-update 和 rn-xwebview README Co-Authored-By: Claude <noreply@anthropic.com>
这个提交包含在:
父节点
0431b3d694
当前提交
8f0a940830
@ -56,7 +56,7 @@ if (pluginInfo.needsUpdate) {
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `UpdateSDK.checkAppUpdate(bypassIgnore?)` | 检查 App 更新(30 分钟缓存) |
|
||||
| `UpdateSDK.downloadApk(info, options?)` | 下载 APK,返回 ArrayBuffer |
|
||||
| `UpdateSDK.downloadApk(info, options?)` | 下载 APK,返回 ArrayBuffer(支持进度回调) |
|
||||
| `UpdateSDK.downloadAndInstallApk(url, options?)` | 下载 APK 并调起系统安装器(Android) |
|
||||
| `UpdateSDK.openStore(appStoreUrl?, marketUrl?)` | 跳转应用商店 |
|
||||
| `UpdateSDK.getAppVersionCode()` | 获取当前 versionCode |
|
||||
@ -67,6 +67,7 @@ if (pluginInfo.needsUpdate) {
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `UpdateSDK.checkPluginUpdate(moduleId)` | 检查插件更新(30 分钟缓存) |
|
||||
| `UpdateSDK.downloadPlugin(moduleId, info, options?)` | 下载插件 bundle,返回 JS 源码(支持进度回调) |
|
||||
| `UpdateSDK.updatePlugin(moduleId, options?)` | 一步完成:检查 → 下载 → 写文件 → 重载 |
|
||||
|
||||
### 类型定义
|
||||
@ -106,6 +107,31 @@ interface CachedRnBundle {
|
||||
downloadedAt: string
|
||||
source: string
|
||||
}
|
||||
|
||||
type UpdateDownloadProgress = {
|
||||
bytesDownloaded: number
|
||||
totalBytes: number
|
||||
/** 0-100 */
|
||||
percent: number
|
||||
}
|
||||
```
|
||||
|
||||
### 下载进度回调
|
||||
|
||||
`downloadApk()` 和 `downloadPlugin()` 支持 `onProgress` 回调,使用 `fetch + ReadableStream` 实现流式进度计算:
|
||||
|
||||
```ts
|
||||
// 下载 APK 带进度
|
||||
const buffer = await UpdateSDK.downloadApk(info, {
|
||||
onProgress: ({ bytesDownloaded, totalBytes, percent }) => {
|
||||
console.log(`下载进度: ${percent}% (${bytesDownloaded}/${totalBytes})`)
|
||||
},
|
||||
})
|
||||
|
||||
// 下载插件 bundle 带进度
|
||||
const source = await UpdateSDK.downloadPlugin('szyx', pluginInfo, {
|
||||
onProgress: ({ percent }) => setProgress(percent),
|
||||
})
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
@ -75,4 +75,59 @@ window.XWebViewBridge.postMessage(JSON.stringify({ type: 'login', token: '...' }
|
||||
controller.postMessageToWeb("window.dispatchEvent(new CustomEvent('nativeMsg', { detail: { key: 'value' } }))")
|
||||
```
|
||||
|
||||
详细 handler 列表见 `@xuqm/rn-common` 的 `xwebview/` 目录和 `docs/XWebView-JSBridge.md`。
|
||||
### 内置 JSBridge Handler
|
||||
|
||||
| Action | 说明 | 参数 |
|
||||
|--------|------|------|
|
||||
| `xuqm.getDeviceInfo` | 获取设备信息 | — |
|
||||
| `xuqm.getToken` | 获取当前用户 token | — |
|
||||
| `xuqm.getUserInfo` | 获取用户信息 | — |
|
||||
| `xuqm.openNativePage` | 打开原生页面 | `{ route, params? }` |
|
||||
| `xuqm.closeWebView` | 关闭 WebView | — |
|
||||
| `xuqm.showToast` | 显示 Toast | `{ message }` |
|
||||
| `xuqm.scanQRCode` | 打开扫码页(需宿主注册 handler) | — |
|
||||
|
||||
## 文件下载
|
||||
|
||||
XWebView 内置文件下载支持,自动拦截以下场景:
|
||||
|
||||
1. URL 以常见下载扩展名结尾(`.pdf`, `.zip`, `.apk` 等)
|
||||
2. `<a download="...">` 标签点击
|
||||
3. `blob:` URL 下载(通过 injected JS 拦截)
|
||||
|
||||
### 使用 `handleDownloadRequest`
|
||||
|
||||
```ts
|
||||
import { handleDownloadRequest } from '@xuqm/rn-xwebview'
|
||||
|
||||
const handle = await handleDownloadRequest(url, hintFilename, {
|
||||
autoDownload: true,
|
||||
downloadConflict: 'rename',
|
||||
onDownloadProgress: (p) => console.log(`${p.percentage}%`),
|
||||
onDownloadComplete: (r) => console.log('saved to', r.filePath),
|
||||
onDownloadError: (url, err) => console.error(err),
|
||||
})
|
||||
// handle?.cancel() 可取消下载
|
||||
```
|
||||
|
||||
### XWebViewConfig 下载相关参数
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `autoDownload` | boolean | 自动下载(默认 true),false 时需 onDownloadDecide |
|
||||
| `downloadConflict` | `'rename' \| 'overwrite'` | 文件名冲突策略(默认 rename) |
|
||||
| `onDownloadStart` | (request) => void | 下载开始回调 |
|
||||
| `onDownloadProgress` | (progress) => void | 下载进度回调 |
|
||||
| `onDownloadComplete` | (result) => void | 下载完成回调 |
|
||||
| `onDownloadError` | (url, error) => void | 下载失败回调 |
|
||||
| `onDownloadDecide` | (request) => Decision | 非自动模式下的下载决策回调 |
|
||||
|
||||
### 低级 API
|
||||
|
||||
| API | 说明 |
|
||||
|-----|------|
|
||||
| `fetchDownloadInfo(url, hintFilename?)` | HEAD 请求获取文件名/MIME/大小 |
|
||||
| `saveBase64File(base64, filename, savePath?, conflict)` | 保存 base64 数据为文件 |
|
||||
| `startDownload(url, filename, savePath?, conflict, onProgress, onComplete, onError)` | 开始下载,返回 DownloadHandle |
|
||||
|
||||
详细 handler 列表见 `@xuqm/rn-common` 的 `xwebview/` 目录。
|
||||
|
||||
@ -2,6 +2,7 @@ import { Platform } from 'react-native'
|
||||
import RNFetchBlob from 'react-native-blob-util'
|
||||
|
||||
import type {
|
||||
XWebViewDownloadDecision,
|
||||
XWebViewDownloadProgress,
|
||||
XWebViewDownloadRequest,
|
||||
XWebViewDownloadResult,
|
||||
@ -105,6 +106,67 @@ export async function saveBase64File(
|
||||
|
||||
export type DownloadHandle = { cancel: () => void }
|
||||
|
||||
/**
|
||||
* 统一的下载请求处理函数。
|
||||
*
|
||||
* 封装 fetchDownloadInfo → onDownloadDecide → startDownload 的完整流程,
|
||||
* 供 XWebViewScreen / XWebViewView 及外部调用方使用。
|
||||
*
|
||||
* @returns DownloadHandle(可取消),若用户拒绝或无需下载则返回 null
|
||||
*/
|
||||
export async function handleDownloadRequest(
|
||||
url: string,
|
||||
hintFilename: string | undefined,
|
||||
options: {
|
||||
autoDownload?: boolean
|
||||
downloadConflict?: 'rename' | 'overwrite'
|
||||
savePath?: string
|
||||
onDownloadDecide?: (
|
||||
request: XWebViewDownloadRequest,
|
||||
) => XWebViewDownloadDecision | Promise<XWebViewDownloadDecision>
|
||||
onDownloadStart?: (request: XWebViewDownloadRequest) => void
|
||||
onDownloadProgress?: (progress: XWebViewDownloadProgress) => void
|
||||
onDownloadComplete?: (result: XWebViewDownloadResult) => void
|
||||
onDownloadError?: (url: string, error: string) => void
|
||||
} = {},
|
||||
): Promise<DownloadHandle | null> {
|
||||
const {
|
||||
autoDownload = true,
|
||||
downloadConflict = 'rename',
|
||||
savePath,
|
||||
onDownloadDecide,
|
||||
onDownloadStart,
|
||||
onDownloadProgress,
|
||||
onDownloadComplete,
|
||||
onDownloadError,
|
||||
} = options
|
||||
|
||||
const info = await fetchDownloadInfo(url, hintFilename)
|
||||
let finalFilename = info.suggestedFilename
|
||||
let finalSavePath = savePath
|
||||
|
||||
// 非自动下载模式:需要宿主决定是否允许
|
||||
if (!autoDownload) {
|
||||
if (!onDownloadDecide) return null
|
||||
const decision = await Promise.resolve(onDownloadDecide(info))
|
||||
if (!decision.allowed) return null
|
||||
if (decision.filename) finalFilename = decision.filename
|
||||
if (decision.savePath) finalSavePath = decision.savePath
|
||||
}
|
||||
|
||||
onDownloadStart?.({ ...info, suggestedFilename: finalFilename })
|
||||
|
||||
return startDownload(
|
||||
url,
|
||||
finalFilename,
|
||||
finalSavePath,
|
||||
downloadConflict,
|
||||
p => onDownloadProgress?.(p),
|
||||
r => onDownloadComplete?.(r),
|
||||
err => onDownloadError?.(url, err),
|
||||
)
|
||||
}
|
||||
|
||||
export function startDownload(
|
||||
url: string,
|
||||
filename: string,
|
||||
|
||||
@ -21,3 +21,5 @@ export type {
|
||||
export { default as XWebViewProgress } from './XWebViewProgress'
|
||||
export { XWebViewView } from './XWebViewView'
|
||||
export { XWebViewScreen } from './XWebViewScreen'
|
||||
export { handleDownloadRequest, fetchDownloadInfo, saveBase64File, startDownload } from './XWebViewDownload'
|
||||
export type { DownloadHandle } from './XWebViewDownload'
|
||||
|
||||
@ -23,7 +23,7 @@ export type {
|
||||
export { PushSDK } from '@xuqm/rn-push'
|
||||
export type { PushVendor } from '@xuqm/rn-push'
|
||||
export { UpdateSDK } from '@xuqm/rn-update'
|
||||
export type { PluginRegistration, PluginMeta, AppUpdateInfo, PluginUpdateInfo, CachedRnBundle } from '@xuqm/rn-update'
|
||||
export type { PluginRegistration, PluginMeta, AppUpdateInfo, PluginUpdateInfo, CachedRnBundle, UpdateDownloadProgress } from '@xuqm/rn-update'
|
||||
export {
|
||||
XWebViewControl,
|
||||
XWebViewScreen,
|
||||
@ -31,8 +31,14 @@ export {
|
||||
getXWebViewConfig,
|
||||
openXWebView,
|
||||
setXWebViewController,
|
||||
setXWebViewScanQRCodeHandler,
|
||||
handleDownloadRequest,
|
||||
fetchDownloadInfo,
|
||||
saveBase64File,
|
||||
startDownload,
|
||||
} from '@xuqm/rn-xwebview'
|
||||
export type {
|
||||
ScanQRCodeHandler,
|
||||
XWebViewClickMenu,
|
||||
XWebViewConfig,
|
||||
XWebViewControllerAPI,
|
||||
@ -42,4 +48,5 @@ export type {
|
||||
XWebViewDownloadResult,
|
||||
XWebViewMessageEvent,
|
||||
XWebViewPermissionRequest,
|
||||
DownloadHandle,
|
||||
} from '@xuqm/rn-xwebview'
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户