React Native安卓端WebView如何监听文件下载事件
React Native安卓端WebView PDF下载捕获方案
已知环境:react-native-webview@^11.15.0,场景为WebView加载链接经多次跳转后触发PDF下载,现有onNavigationStateChange匹配后缀的方案无法覆盖无后缀、响应头触发下载的场景,官方提供的文件下载回调仅支持iOS,安卓端无默认实现。
方案1:原生层加下载监听(推荐,稳定性100%)
安卓系统WebView本身自带下载监听接口,只是react-native-webview没有把这个接口封装到JS层,手动加几行代码就能覆盖所有下载场景,不管跳多少次、链接带不带pdf后缀都能抓到:
- 找到文件路径
node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java - 在WebView实例初始化的代码块里,添加如下下载监听逻辑:
webView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { // 匹配PDF类型,要支持其他下载类型直接在这里加判断即可 if ("application/pdf".equals(mimetype) || url.contains(".pdf")) { // 两种处理方式二选一: // 方式一:直接唤起系统下载器/浏览器打开PDF,无需额外编写下载逻辑 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); ((ReactContext)webView.getContext()).startActivity(intent); // 方式二:把下载事件抛回JS层处理,比如用rn-fetch-blob实现自定义下载、对接自定义PDF预览组件 // WritableMap event = Arguments.createMap(); // event.putString("pdfUrl", url); // ((RNCWebView) webView).emit("androidPdfDownload", event); } } });
- 如果选择事件抛回JS层的处理方式,在WebView组件上添加对应事件监听即可:
<WebView onNavigationStateChange={handleWebViewNavigationStateChange} source={{uri: my_url_link}} style={styles.webviewStyles} onAndroidPdfDownload={(e) => { const pdfUrl = e.nativeEvent.pdfUrl; // 编写自定义下载、预览逻辑 console.log('捕获到PDF下载链接', pdfUrl); }} />
实用提示:直接修改node_modules下的代码在重装依赖时会丢失,可使用
patch-package把该改动打成补丁存入项目,团队成员安装依赖时会自动应用补丁,无需额外编写独立原生模块。
方案2:JS层拦截请求判断(无原生改动,适合快速上线)
不想改动原生代码的话,可以利用onShouldStartLoadWithRequest回调拦截所有跳转请求,通过链接后缀匹配、发送HEAD请求校验响应头Content-Type的方式识别PDF资源:
- 给WebView组件添加
onShouldStartLoadWithRequest回调,实现拦截逻辑:
const handleShouldStartLoad = async (request) => { const { url } = request; // 优先匹配带.pdf后缀的链接,减少不必要的网络请求 if (url.includes('.pdf')) { console.log('捕获到PDF链接', url); // 处理下载/预览逻辑,返回false阻止WebView直接加载该资源 return false; } // 对无pdf后缀的链接,发送HEAD请求校验响应类型 try { const headRes = await fetch(url, { method: 'HEAD' }); const contentType = headRes.headers.get('Content-Type'); if (contentType?.includes('application/pdf')) { console.log('捕获到无后缀PDF链接', url); // 处理下载/预览逻辑 return false; } } catch (err) { // 请求校验失败时直接放行,避免影响正常页面跳转流程 } return true; };
- 将回调挂载到WebView组件上:
<WebView onNavigationStateChange={handleWebViewNavigationStateChange} onShouldStartLoadWithRequest={handleShouldStartLoad} source={{uri: my_url_link}} style={styles.webviewStyles} />
注意:该方案存在兼容性缺陷,如果目标站点配置了防盗链、跨域限制,HEAD请求会被拦截导致判断失效,仅适合站点规则完全可控的内部业务场景。
内容的提问来源于stack exchange,提问作者Miguel Angel Ortega
相关产品推荐
相关产品推荐

