HarmonyOS 鸿蒙Next中webview打开h5链接下载问题
HarmonyOS 鸿蒙Next中webview打开h5链接下载问题 我现在的问题是用鸿蒙自带的webview打开一个h5页面,这个h5页面有个下载动作,是js自己的下载动作,无鸿蒙提供方法支持,这里想实现的就是纯h5下载, 目前现状是点击了没反应,是在鸿蒙的webview里必须下载鸿蒙的包吗 不能下载其他的包吗?还是怎么,请各位大佬有知道的麻烦说下,谢谢了
Web 组件里 H5 触发下载后,应用侧需要接管下载任务。官方 ArkWeb 文档说明,应通过 WebDownloadDelegate.onBeforeDownload 接收下载开始前的回调,并在回调中调用 WebDownloadItem.start(“保存路径”)。如果不调用 start 并提供下载路径,下载任务会保持 PENDING。
实现方式是:创建 WebDownloadDelegate,在 onBeforeDownload 里取 item.getSuggestedFileName() 拼接应用可写路径,例如应用沙箱 cache 目录,然后调用 item.start(path),最后通过 controller.setDownloadDelegate(delegate) 设置下载代理。这个机制和下载的是否是鸿蒙安装包无关,普通 H5 文件下载也需要这样处理。
依据:
更多关于HarmonyOS 鸿蒙Next中webview打开h5链接下载问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
这个不是“只能下载鸿蒙包”的问题,本质上是 ArkWeb/WebView 对 H5 下载行为默认不会自动接管。
很多 H5 的下载:
window.location.href = xxx
或者:
a.download
blob
application/octet-stream
在普通浏览器里会触发下载。
但是 HarmonyOS WebView 默认:
- 不会自动帮你下载文件
- 需要宿主侧接管下载事件
- 否则就是“点击没反应”
所以:
不是不能下载 apk/zip/pdf/exe 之类文件。
而是:
WebView 没有实现下载监听。
———————— 解决方案:
需要监听:
onDownloadStart
然后自己处理下载逻辑。
例如:
Web({
src: this.url,
controller: this.controller
})
.onDownloadStart((event) => {
console.info('下载链接:' + event.url)
// 可以调用系统浏览器
// 或自己下载
})
event 里面一般会有:
event.url
event.userAgent
event.contentDisposition
event.mimetype
event.contentLength
———————— 常见处理方式:
方案1(最简单):
直接拉起系统浏览器下载。
import common from '@ohos.app.ability.common';
import wantConstant from '@ohos.app.ability.wantConstant';
let want = {
action: 'ohos.want.action.viewData',
uri: event.url
}
context.startAbility(want)
这个最稳。
很多商业 App 都这么干。
———————— 方案2:
自己实现下载器。
使用:
@ohos.request
或者:
http.createHttp()
下载文件。
适合:
- 显示下载进度
- 断点续传
- 自定义保存目录
———————— 另外有几个坑。
1、blob 下载
很多 Vue/React H5:
URL.createObjectURL(blob)
ArkWeb 某些版本不支持 blob download。
表现就是:
点击无反应。
这种一般要:
前端改成真实下载链接。
例如:
不要:
blob:
改成:
https://xxx/test.apk
———————— 2、a.download 属性
ArkWeb 对:
<a download>
兼容性一般。
尤其:
- canvas导出
- excel导出
- blob导出
经常失效。
建议前端:
改后端真实文件流。
———————— 3、跨域问题
有些下载链接:
- cookie鉴权
- referer校验
- cors限制
WebView 请求不到。
这时候:
系统浏览器能下载
WebView 不能下载
非常常见。
———————— 总结一句:
鸿蒙 WebView 不是不能下载别的包。
而是:
H5 下载行为默认不会自动处理。
正确做法是:
.onDownloadStart(...)
宿主接管下载。
用ArkWeb的webView的话,要下载网页里的下载,需要通过WebDownloadDelegate来操作。
这个用arkWeb打开网页,下载保存到手机存储,支持用户选择手机存储路径。
参考《这个》文章的示例。
不是啊,并非只能下载鸿蒙相关的包。WebView需要应用侧主动设置下载委托(WebDownloadDelegate)来拦截并处理下载请求,才能实现任意文件(如APK、文档、图片等)的下载。
你先为WebView设置WebDownloadDelegate,并在其回调中启动下载、指定保存路径。
先确保权限已经加了
"permissions": [
"ohos.permission.INTERNET",
"ohos.permission.READ_USER_STORAGE",
"ohos.permission.WRITE_USER_STORAGE"
]
在给 WebView 绑定 DownloadDelegate
import web_webview from '@ohos.web.webview';
import business_error, { BusinessError } from '@ohos.base';
import { picker } from '@kit.CoreFileKit';
import fs from '@ohos.file.fs';
import { fileUri } from '@kit.CoreFileKit';
@Entry
@Component
struct WebDownloadFilePage {
controller: web_webview.WebviewController = new web_webview.WebviewController();
delegate: web_webview.WebDownloadDelegate = new web_webview.WebDownloadDelegate();
build() {
Column() {
Button('setDownloadDelegate')
.onClick(() => {
try {
this.delegate.onBeforeDownload((webDownloadItem: web_webview.WebDownloadItem) => {
console.log("======will start a download." + webDownloadItem.getSuggestedFileName());
webDownloadItem.start("/data/storage/el2/base/cache/web/" + webDownloadItem.getSuggestedFileName());
})
this.delegate.onDownloadUpdated((webDownloadItem: web_webview.WebDownloadItem) => {
console.log("======download update guid: " + webDownloadItem.getGuid());
})
this.delegate.onDownloadFailed((webDownloadItem: web_webview.WebDownloadItem) => {
console.log("download failed guid: " + webDownloadItem.getGuid());
})
this.delegate.onDownloadFinish((webDownloadItem: web_webview.WebDownloadItem) => {
console.log("======download finish guid: " + webDownloadItem.getGuid());
console.log("======downloadFullPath:" + webDownloadItem.getFullPath())
let documentSaveOptions = new picker.DocumentSaveOptions();
documentSaveOptions.newFileNames = [webDownloadItem.getSuggestedFileName()];
let documentPicker = new picker.DocumentViewPicker();
documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD;
documentPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
let uri = documentSaveResult$1;
let path: string = new fileUri.FileUri(uri).path
let sanPath = "/data/storage/el2/base/cache/web/" + webDownloadItem.getSuggestedFileName()
let sanFile = fs.openSync(sanPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
let pubFile = fs.openSync(path + '/***.pdf', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
fs.copyFileSync(sanFile.fd, pubFile.fd)
console.info('DocumentViewPicker.save successfully, DocumentSaveResult uri: ' + JSON.stringify(documentSaveResult));
}).catch((err: BusinessError) => {
console.error('DocumentViewPicker.save failed with err: ' + JSON.stringify(err));
});
})
this.controller.setDownloadDelegate(this.delegate);
} catch (error) {
let e: business_error.BusinessError = error as business_error.BusinessError;
console.error(`ErrorCode: ${e.code}, Message: ${e.message}`);
}
})
Button('startDownload')
.onClick(() => {
try {
this.controller.startDownload('https://***.pdf');
} catch (error) {
let e: business_error.BusinessError = error as business_error.BusinessError;
console.error(`ErrorCode: ${e.code}, Message: ${e.message}`);
}
})
Web({ src: 'www.huawei.com', controller: this.controller })
.fileAccess(true)
.javaScriptAccess(true)
.domStorageAccess(true)
}.margin({ top: 200 })
}
}
你好,监听下载后使用DocumentViewPicker拉起保存,参考使用ArkWeb下载文件提示完成但文件为何为空
- 监听页面触发的下载:通过setDownloadDelegate()向Web组件注册一个DownloadDelegate来监听页面触发的下载任务。资源由Web组件进行下载,Web组件会通过DownloadDelegate将下载的进度通知给应用。
- [@ohos.file.picker](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-picker):选择器(Picker)是一个封装DocumentViewPicker、AudioViewPicker、PhotoViewPicker的API模块,具有选择与保存的能力。
- DocumentSaveOptions:文档保存选项。DocumentSaveOptions是选择器(@ohos.file.picker)模块的核心配置类,用于定制文件保存操作的行为。
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
import { fileUri, picker } from '@kit.CoreFileKit';
@Entry
@Component
struct WebComponent {
controller: webview.WebviewController = new webview.WebviewController();
delegate: webview.WebDownloadDelegate = new webview.WebDownloadDelegate();
build() {
Column() {
Button('setDownloadDelegate')
.onClick(() => {
// 下载开始前通知给用户,用户需要在此接口中调用WebDownloadItem.start("xxx")并提供下载路径,否则下载会一直处于PENDING状态。
this.delegate.onBeforeDownload((webDownloadItem: webview.WebDownloadItem) => {
console.info('EntryAbility: will start a download.');
const documentSaveOptions = new picker.DocumentSaveOptions();
// 修改pickerMode为DOWNLOAD,删除newFileNames和fileSuffixChoices,设置为DOWNLOAD时,配置的参数newFileNames和fileSuffixChoices将不会生效
documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD;
let uris: Array<string> = [];
let documentViewPicker = new picker.DocumentViewPicker();
documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
// 固定返回当前HAP包名的文件夹
uris = documentSaveResult;
if (0 == uris.length) {
return;
}
console.info(`EntryAbility: documentViewPicker.save to file succeed and uris are:${uris}`);
const uriString = documentSaveResult[0];
if (!uriString) {
return;
}
// 添加文件路径转换
const uri = new fileUri.FileUri(uriString);
webDownloadItem.start(uri.path + '/fileName_' + (new Date()).getTime() + '.txt');
console.info(`EntryAbility: download to ${uri.path}`);
}).catch((err: BusinessError) => {
console.error(`EntryAbility: Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
});
});
this.controller.setDownloadDelegate(this.delegate);
});
Button('startDownload')
.onClick(() => {
try {
// 运行时需替换为实际的链接
this.controller.startDownload('XXX');
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
});
// 运行时需替换为实际的链接
Web({ src: 'XXX', controller: this.controller }).fileAccess(false).geolocationAccess(false);
};
}
}
Web 组件不会像系统浏览器一样自动把所有 H5 下载动作接管并落盘,所以点击 H5 里的下载按钮没反应,通常不是“只能下载鸿蒙包”或文件类型限制,而是宿主应用没有处理下载链路。
普通 http/https 文件链接可以先在 Web 上监听 onDownloadStart,回调里能拿到 url、userAgent、contentDisposition、mimetype、contentLength 等信息;如果还需要进度/开始前控制,可以再用 WebDownloadDelegate 这类下载委托能力接管。拿到下载地址后,由 ArkTS 侧用 request.downloadFile 或网络请求下载到应用沙箱,再通过 Picker/SaveButton 等方式让用户保存到外部位置。
如果 H5 是 blob:、base64、a[download] 或 JS 动态生成文件,ArkTS 侧可能拿不到一个真实的可下载 URL,这种情况建议让 H5 通过 JSBridge(registerJavaScriptProxy / javaScriptProxy)把后端下载 URL 或文件内容传给应用侧,再由应用侧保存。还要确认 module.json5 里有 INTERNET 权限,保存到用户可见目录时走系统授权/安全控件。
在 HarmonyOS Next 的 WebView 中,出于安全与生态管控要求,默认禁止 H5 直接触发非应用商店来源的“自由下载”。所以纯 JS 下载动作(如通过 a 标签 download 属性或 blob 生成链接点击)可能无响应,并不是必须下载鸿蒙包,而是需要原生端配合拦截并接管下载流程。
解决办法: 在原生侧通过 onDownloadStart 或 onLoadIntercept 事件监听 WebView 的下载意图,再使用 @ohos.request 的 download 接口将文件写入沙箱或公共目录,完成后可通知用户。例如简单示例:
webview.on('downloadStart', (event) => {
// 通过 event.url 获取下载地址
request.downloadFile(context, {
url: event.url,
filePath: '/path/to/save' // 沙箱路径
}).then(() => {
// 下载完成
});
// 阻止 WebView 默认行为
event.preventDefault();
});
这样就能实现类似纯 H5 下载的效果,但实际依赖原生能力。


