You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Java在Android WebView中启用文件上传下载功能

Android WebView 文件上传、下载功能Java配置方案

前置准备:清单权限配置

先在AndroidManifest.xml中声明功能必需的权限:

<!-- 基础网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Android 12及以下读取本地文件权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Android 13+ 媒体文件读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- Android 9及以下写入存储权限,用于保存下载文件 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />

Android 6.0及以上版本的危险权限需要在代码中动态申请,必须拿到权限后再触发上传、下载逻辑。


基础WebView配置

先初始化WebSettings,打开基础能力开关:

WebSettings webSettings = webView.getSettings();
// 启用JavaScript,绝大多数网页的上传、下载交互依赖JS
webSettings.setJavaScriptEnabled(true);
// 开启本地文件访问权限
webSettings.setAllowFileAccess(true);
// 开启DOM存储,避免网页本地存储异常导致功能失效
webSettings.setDomStorageEnabled(true);
// 兼容HTTPS页面加载HTTP资源
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}

启用文件下载能力

WebView默认不会接管下载事件,需要设置DownloadListener自定义处理逻辑,以下是对接系统下载管理器的实现:

webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
    // 解析下载文件名
    String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype);
    request.setMimeType(mimetype);
    request.addRequestHeader("User-Agent", userAgent);
    request.setTitle(fileName);
    request.setDescription("文件下载中");
    // 允许移动网络、WiFi环境下下载
    request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
    // 下载全程在通知栏显示进度
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    // 文件保存到系统公共下载目录
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);

    DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
    downloadManager.enqueue(request);
    Toast.makeText(this, "已开始下载文件", Toast.LENGTH_SHORT).show();
});
  • 简单场景可以直接替换为跳转系统浏览器处理下载,无需自行申请存储权限
  • Android 10及以上使用分区存储,写入公共下载目录不需要额外申请存储权限

启用文件上传能力

WebView的文件选择能力通过WebChromeClient的隐藏回调实现,需要重写对应方法接收选择的文件并回传给网页:

  1. 先在Activity中定义回调持有变量
// 持有网页的文件选择回调
private ValueCallback<Uri[]> filePathCallback;
private static final int FILE_CHOOSER_REQ_CODE = 10001;
  1. 给WebView设置自定义WebChromeClient
webView.setWebChromeClient(new WebChromeClient() {
    // Android 5.0及以上版本走该回调
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> callback, FileChooserParams params) {
        // 清空上一次未处理的回调,避免重复点击无响应
        if (filePathCallback != null) {
            filePathCallback.onReceiveValue(null);
        }
        filePathCallback = callback;

        // 构造文件选择Intent
        Intent chooseIntent = new Intent(Intent.ACTION_GET_CONTENT);
        chooseIntent.addCategory(Intent.CATEGORY_OPENABLE);
        // 支持多选
        chooseIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, params.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE);
        // 匹配网页要求的文件类型
        String[] acceptTypes = params.getAcceptTypes();
        chooseIntent.setType("*/*");
        if (acceptTypes != null && acceptTypes.length > 0 && !TextUtils.isEmpty(acceptTypes[0])) {
            chooseIntent.setType(String.join(" ", acceptTypes));
        }

        startActivityForResult(Intent.createChooser(chooseIntent, "选择上传文件"), FILE_CHOOSER_REQ_CODE);
        return true;
    }

    // Android 4.4及以下版本兼容回调,可根据目标版本按需实现
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {}
});
  1. 重写onActivityResult接收文件选择结果,回传给WebView
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == FILE_CHOOSER_REQ_CODE) {
        if (filePathCallback == null) return;
        Uri[] resultUris = null;
        if (resultCode == RESULT_OK && data != null) {
            // 处理多选文件
            if (data.getClipData() != null) {
                int count = data.getClipData().getItemCount();
                resultUris = new Uri[count];
                for (int i = 0; i < count; i++) {
                    resultUris[i] = data.getClipData().getItemAt(i).getUri();
                }
            } 
            // 处理单选文件
            else if (data.getData() != null) {
                resultUris = new Uri[]{data.getData()};
            }
        }
        // *必须调用该方法回传结果,即使用户取消选择也要传null,否则后续点击上传按钮会无响应*
        filePathCallback.onReceiveValue(resultUris);
        filePathCallback = null;
    }
}

常见问题排查

  • 上传按钮点击无反应:检查是否漏写onReceiveValue回调,是否动态申请了文件读取权限
  • 下载无响应:检查是否设置了DownloadListener,是否声明了网络权限
  • 选择文件后网页无反应:检查是否正确解析了返回的Uri,不要自定义修改Uri结构直接回传即可

内容的提问来源于stack exchange,提问作者vishvajith abegunasekara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 06:34:03