从 android 13 下载 WebView 中的文件

问题描述 投票:0回答:1

我的 Android (13) 应用程序中有一个网络视图。当用户转到网络视图并单击链接下载文件时,什么也没有发生。

    downloadButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String link = linkEditText.getText().toString().trim();
            if (!link.isEmpty()) {
                urlWrapper.iframeUrl = "https://convert2mp3s.com/api/widgetv2?url=" + link;
                webView.loadDataWithBaseURL(null, "<iframe id=\"widgetApi\" src=\"" + urlWrapper.iframeUrl + "\" width=\"100%\" height=\"100%\" allowtransparency=\"true\" scrolling=\"no\" style=\"border:none\"></iframe>", "text/html", "utf-8", null);
            }
        }

添加额外权限: WRITE_EXTERNAL_STORAGE、READ_EXTERNAL_STORAGE、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO、READ_MEDIA_IMAGES、ACCESS_NETWORK_STATE、ACCESS_WIFI_STATE、WRITE_EXTERNAL_STORAGE、DOWNLOAD_WITHOUT_NOTIFICATION、ACCESS_DOWNLOAD_MANAGER

仍然没有任何作用。

如何在android 13中从webview下载文件? 如果将其切换到界面浏览器,它就可以工作。

java android download storage
1个回答
3
投票

TL;DR WebView

 上设置下载侦听器
,并使用
DownloadManager
将文件下载到 外部下载目录

下载中:

  1. 将 URL 字符串解析为
    Uri
    对象。
  2. 提取路径的最后一部分,可能是文件名。通过检查文件名称中是否包含点来检查文件是否有扩展名也是一个好主意,如果不包含,您可以使用 MimeTypeMap
     来根据 
    mimeType 获取扩展名
    .
    构造一个
  3. Request
     对象并将目标设置为外部 
    Downloads 目录
    。还要设置 User-Agent
     以保持与 
    WebView
    发送的请求的一致性,并在
    下载完成时显示 
    通知,以便用户知道它已完成。 获取
  4. DownloadManager
  5. 系统服务并
    开始下载
    WebView webView = findViewById(R.id.web_view); webView.loadUrl("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"); webView.setDownloadListener((url, userAgent, contentDisposition, mimeType, contentLength) -> { Uri uri = Uri.parse(url); String[] path = uri.getPath().split("/"); String fileName = path[path.length - 1]; if(!fileName.contains(".")) { fileName += MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType); } DownloadManager.Request request = new DownloadManager.Request(uri); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) request.addRequestHeader("User-Agent", userAgent); DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); downloadManager.enqueue(request); });
权限处理:
这将有效,但仅限于运行 

API 级别 29+

的 Android 设备,因为在任何旧版本上,您的应用程序都需要拥有

WRITE_EXTERNAL_STORAGE 权限才能写入

共享 Downlaods 目录
,即使是通过DownloadManager。如果您想支持旧版本(您确实应该),请按照以下步骤操作:
在您的

    Manifest
  1. 中声明权限,记住,它
    仅适用于API级别
    <=28<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
在 Activity 中添加 
uri, userAgent and mimeType
    作为全局变量,以便它们可以在其他函数中使用
  1. public class MainActivity extends AppCompatActivity {
        private Uri downloadUri;
        private String downloadUserAgent;
        private String downloadMimeType;
    
        ...
    
将下载逻辑抽象为单独的函数,以便可以重用并使其使用上一步中的全局变量。
  1. private void download() { String[] path = downloadUri.getPath().split("/"); String fileName = path[path.length - 1]; if(!fileName.contains(".")) { fileName += MimeTypeMap.getSingleton().getExtensionFromMimeType(downloadMimeType); } DownloadManager.Request request = new DownloadManager.Request(downloadUri); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.addRequestHeader("User-Agent", downloadUserAgent); DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); downloadManager.enqueue(request); }
setDownloadListener
    中为全局变量赋值。检查设备 API 级别,因为如前所述,我们只需要 API 级别
  1. 低于 29
     的权限,并且 
    权限仅在运行时在大于 22 的 API 级别上授予(在安装时授予权限之前) 。如果已经授予许可,只需开始下载,否则请求它 webView.setDownloadListener((url, userAgent, contentDisposition, mimeType, contentLength) -> { downloadUri = Uri.parse(url); downloadUserAgent = userAgent; downloadMimeType = mimeType; // Permission is only required for API level <= 28 && Permissions are granted at runtime only for API level 23 and above if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.P && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { boolean hasWriteExternalStoragePermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; if(!hasWriteExternalStoragePermission) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); } else { download(); } } else { download(); } });
通过重写
onRequestPermissionsResult
  1. 函数来处理权限请求结果,并首先检查request code
    是否为
    0
    ,这是为了确保权限结果引用我们之前创建的
    permission request
    (我们已经设置了) 0 作为那里的请求代码),在同一条语句中我们还检查是否有实际结果需要检查。之后,我们检查第一个(在我们的例子中是唯一的)权限是否已变为
    授予
    ,如果是,我们再次开始下载,但如果不是,那么我们还有更多工作要做。如果用户拒绝它,我们检查shouldShowRequestPermissionRationale是否返回false,这可能会令人困惑,但我们本质上所做的是检查用户是否永久拒绝
    该权限,如果他们有,我们向他们展示一个小对话框,告诉他们这是“文件下载所必需的”,并将他们引导至“设置”,在那里他们可以“启用它”。附:您可以抑制 
    newAPI
    ,因为它永远不会在“旧 API 级别”上被调用,因为我们在步骤 4 中实现了 if 检查。 @SuppressLint("NewApi") @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode == 0 && grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { download(); } else if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { new AlertDialog.Builder(this) .setMessage("You permanently declined the storage permission witch is required to download this file. If you want to download it go to the settings and allow the storage permission.") .setPositiveButton("Settings", (DialogInterface p0, int p1) -> { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri intentUri = Uri.fromParts("package", getPackageName(), null); intent.setData(intentUri); startActivity(intent); }) .setNegativeButton("Cancel", (DialogInterface p0, int p1) -> {}) .show(); } } } 饼干: 我知道这已经是一个很长的答案了,但我觉得有必要再告诉你一件事,那就是如果你的文件需要身份验证才能访问
    该怎么办,以及如何从
    WebView
  2. 传递
cookies
DownloadManager

要获取 cookie,您可以使用 CookieManager。它将返回格式化的字符串,您可以将其直接放入

cookie header
中。你需要小心,因为如果没有 cookie 或者出现
bug
(在代码注释中描述),

CookieManager

会返回

null
private void download() { String[] path = downloadUri.getPath().split("/"); String fileName = path[path.length - 1]; if(!fileName.contains(".")) { fileName += MimeTypeMap.getSingleton().getExtensionFromMimeType(downloadMimeType); } String cookies = CookieManager.getInstance().getCookie(downloadUri.toString()); // There is a bug in certain versions of WebView related to same-site cookies not being returned by the CookieManager // so if you know you will only be downloading files behind authentication you should here probably display a dialog saying the file cannot be downloaded and request the user to update there WebView /*if(cookies == null) { new AlertDialog.Builder(this) .setMessage("The file cannot be downloaded, try updating your WebView.") .setPositiveButton("Okay", (DialogInterface p0, int p1) -> {}) .show(); return; }*/ DownloadManager.Request request = new DownloadManager.Request(downloadUri); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); if(cookies != null) { request.addRequestHeader("cookie", cookies); } request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.addRequestHeader("User-Agent", downloadUserAgent); DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); downloadManager.enqueue(request); }
如果您想查看完整的工作实现
,我创建了一个
git 存储库

binding.webView.setDownloadListener { url, _, _, _, _ ->
      
        binding.webView.loadUrl(url)
    }

© www.soinside.com 2019 - 2024. All rights reserved.