查看: 345|回复: 3

HarmonyOS 6.1.1 ArkWeb下载溯源:getOrigin

[复制链接]
发表于 昨天 09:00 | 显示全部楼层 |阅读模式
在混合开发与浏览器架构中,下载拦截和管控能力直接体现内核的控制力。用户点击下载链接时,请求往往经历多次301/302重定向,服务端还可能用防盗链机制校验Referrer。过去在HarmonyOS上,开发者通过ArkWeb的WebDownloadItem拦截下载任务时,只能拿到最终地址或数据流,难以做源头审计,也无法在自研下载器中透传原始上下文。

HarmonyOS NEXT 6.1.1(API 24)针对ArkWeb下载控制模块新增了两个关键接口:getOriginalUrl()还原发起下载的初始URL,忽略中间重定向;getReferrerUrl()提取引发下载的引荐页地址,为第三方下载器补充防盗链所需的Referer字段。这两个接口属于@kit.ArkWeb套件中的下载委托层WebDownloadDelegate,仅在Stage模型下可用,无需申请特殊权限。

接口声明如下:
  1. // 获取下载文件的原始URL(重定向前用户点击或系统请求的第一现场地址)
  2. getOriginalUrl(): string;
  3. // 获取下载文件的referrer地址(用户点击下载链接前停留的网页)
  4. getReferrerUrl(): string;
复制代码

实际开发中,典型的痛点是:很多大厂的下载链接采用统一分发调度,例如用户点击 https://d.example.com/latest,服务端返回302跳转到带签名和CDN节点的真实长链接。如果开发者接管下载后只拿得到真实长链接,无法与业务中台核对发版配置,统计归因也会失真。getOriginalUrl能够直接返回用户最初点击的/latest地址,让业务侧看清下载源头。

另一个高频场景是防盗链。企业私有附件系统通常设置严格的Referrer校验,如果直接把WebDownloadItem中的最终链接交给request模块做断点续传,会因缺少原始上下文而收到403。通过getReferrerUrl()拿到引荐页地址后,在自定义HTTP请求头中填入Referer字段,即可正常通过防盗链校验。

下面用一个完整的Demo说明接入流程。页面包含输入框和按钮,隐藏一个Web实例承载内核。核心逻辑在setupDownloadDelegate中:挂载WebDownloadDelegate,在onBeforeDownload回调里拦截下载任务,调用新增接口打印溯源信息,最后指定沙盒路径调用start方法启动下载。
  1. import { webview } from '@kit.ArkWeb';
  2. import { BusinessError } from '@kit.BasicServicesKit';
  3. @Entry
  4. @Component
  5. struct ArkWebDemo {
  6.   controller: webview.WebviewController = new webview.WebviewController();
  7.   delegate: webview.WebDownloadDelegate = new webview.WebDownloadDelegate();
  8.   @State logs: string[] = [];
  9.   @State inputUrl: string = 'https://www.example.com/download/test.zip';
  10.   private appendLog(msg: string) {
  11.     let now = new Date();
  12.     let timeStr = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}.${now.getMilliseconds()}`;
  13.     this.logs.unshift(`[${timeStr}] ${msg}`);
  14.   }
  15.   aboutToAppear() {
  16.     this.appendLog('ArkWeb 下载溯源控制舱已初始化');
  17.     this.setupDownloadDelegate();
  18.   }
  19.   setupDownloadDelegate() {
  20.     try {
  21.       this.delegate.onBeforeDownload((webDownloadItem: webview.WebDownloadItem) => {
  22.         // 获取未发生重定向前的起始请求地址
  23.         let originalUrl = webDownloadItem.getOriginalUrl();
  24.         // 获取触发下载事件的引荐页面来源
  25.         let referrerUrl = webDownloadItem.getReferrerUrl();
  26.         let guid = webDownloadItem.getGuid();
  27.         let fileName = webDownloadItem.getSuggestedFileName();
  28.         this.appendLog(`拦截到下载请求 [Guid: ${guid}]`);
  29.         this.appendLog(`原始触发 URL: ${originalUrl}`);
  30.         this.appendLog(`Referrer 溯源: ${referrerUrl}`);
  31.         this.appendLog(`建议文件名: ${fileName}`);
  32.         // 拼接沙盒下载路径,写入应用el2 cache目录
  33.         let downloadPath = "/data/storage/el2/base/cache/web/" + fileName;
  34.         this.appendLog(`开始写入沙盒路径: ${downloadPath}`);
  35.         // 允许内核接管写入流程,必须显式调用
  36.         webDownloadItem.start(downloadPath);
  37.       });
  38.       this.delegate.onDownloadUpdated((item) => this.appendLog(`进度: ${item.getPercentComplete()}%`));
  39.       this.delegate.onDownloadFailed((item) => this.appendLog(`下载失败 [Guid: ${item.getGuid()}]`));
  40.       this.delegate.onDownloadFinish((item) => this.appendLog(`下载完成 [Guid: ${item.getGuid()}]`));
  41.       this.controller.setDownloadDelegate(this.delegate);
  42.       this.appendLog('下载委托监听已挂载');
  43.     } catch (error) {
  44.       let err = error as BusinessError;
  45.       this.appendLog(`委托挂载失败 Msg: ${err.message}`);
  46.     }
  47.   }
  48.   triggerDownload() {
  49.     try {
  50.       this.appendLog(`尝试触发下载: ${this.inputUrl}`);
  51.       this.controller.startDownload(this.inputUrl);
  52.     } catch (error) {
  53.       let err = error as BusinessError;
  54.       this.appendLog(`触发下载异常 Msg: ${err.message}`);
  55.     }
  56.   }
  57.   build() {
  58.     Column() {
  59.       Text('ArkWeb 下载溯源防线').fontSize(22).fontWeight(FontWeight.Bold).margin({ top: 40, bottom: 20 })
  60.       TextInput({ text: this.inputUrl }).onChange((val) => this.inputUrl = val).width('90%').margin({ bottom: 10 })
  61.       Button('触发下载并溯源').onClick(() => this.triggerDownload()).backgroundColor('#007DFF').width('90%').margin({ bottom: 20 })
  62.       List({ space: 8 }) {
  63.         ForEach(this.logs, (log: string) => {
  64.           ListItem() { Text(log).fontSize(12).fontFamily('monospace').fontColor('#333333') }
  65.         }, (log: string) => log)
  66.       }.width('90%').height('40%').backgroundColor('#EFEFEF').padding(10).borderRadius(8)
  67.       Web({ src: 'www.example.com', controller: this.controller }).width(1).height(1).visibility(Visibility.Hidden)
  68.     }.width('100%').height('100%')
  69.   }
  70. }
复制代码

运行Demo后,点击“触发下载并溯源”,日志会依次展示:委托挂载成功、触发下载的URL、onBeforeDownload回调中提取出的原始URL与Referrer、建议文件名,以及沙盒写入路径,最终显示下载完成。在直连无重定向的测试环境下,原始URL和Referrer可能一致或为空;在真实网页嵌套和重定向场景中,这两个值能精确刻画用户的点击跳转图谱。

需要特别注意的是生命周期闭环。onBeforeDownload回调拦截WebDownloadItem后,相当于完全劫持了下载流程,必须显式调用start(path)传入合法写入路径;如果决定不下载,也要做相应销毁处理。拦截后既不调用start也不做任何清理,内核引擎会陷入等待流状态,可能诱发底层资源泄漏甚至崩溃。

HarmonyOS 6.1.1通过这两个接口补齐了ArkWeb下载链路的溯源闭环。应用侧既能摆脱无法感知前置重定向的被动局面,又能在接管下载时复刻HTTP上下文,安全控制颗粒度明显提升。对于需要自研下载器、统计归因或对接防盗链系统的团队,这两个API值得尽快适配验证。
回复

使用道具 举报

发表于 昨天 09:05 | 显示全部楼层

Re: HarmonyOS 6.1.1 ArkWeb下载溯源:getOrigin

这个接口加得确实实用,之前处理重定向下载只能拿最终地址,查问题经常要自己拼日志还原现场。有了getOriginalUrl,至少能定位用户最初点了什么,跟业务侧核对配置也方便。 防盗链那个场景太真实了,自研下载器经常因为Referrer被拦,原来还得自己从Web里取上下文,现在委托层直接给了,省了不少事。楼主那个Demo流程也很清晰,onBeforeDownload里先溯源再start,逻辑挺完整的。等有空在API 24上试试,看看重定向多级之后返回的originalUrl是不是真的第一跳。
回复 支持 反对

使用道具 举报

发表于 昨天 09:05 | 显示全部楼层

Re: HarmonyOS 6.1.1 ArkWeb下载溯源:getOrigin

感谢分享!这个接口太实用了,尤其是getOriginalUrl,之前处理那些重定向下载链接时确实只能拿到最终地址,业务侧做归因统计特别麻烦。现在直接在拦截回调里就能拿到第一现场,省了很多手工拼接和回溯的工作。 另外防盗链场景也很真实,我们之前就遇到过类似情况,拿最终链接去请求结果被403,还得额外从请求头里扒Referer。这两个接口配合`onBeforeDownload`,能少写不少糙代码。 想问几个细节:`start(downloadPath)`里的路径如果目录不存在,ArkWeb会不会自动创建?还是需要开发者自己先mkdir?还有`getReferrerUrl()`是不是永远返回用户停留页,如果页面是通过JS的`location.href`触发的下载,还能拿到吗?期待楼主后续能再讲讲这些边界场景。
回复 支持 反对

使用道具 举报

发表于 昨天 09:05 | 显示全部楼层

Re: HarmonyOS 6.1.1 ArkWeb下载溯源:getOrigin

这个接口补得挺及时,之前处理重定向下载确实只能拿到最终地址,业务侧想做归因还得自己维护映射表,麻烦得很。现在getOriginalUrl直接给到初始URL,省了不少事。 另外getReferrerUrl对防盗链场景太实用了,以前自己拼Referer还得从Web页面里手动注入,现在委托层直接透传,第三方下载器接起来干净多了。Stage模型限定倒是没问题,反正新应用都推荐用Stage。 有个小疑问:`onDownloadUpdated`这个回调是不是下载过程中会频繁触发?如果文件大,日志刷屏会不会影响性能?还是说本身有节流机制?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-8-6 05:14 , Processed in 0.021341 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部