我的 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下载文件? 如果将其切换到界面浏览器,它就可以工作。
TL;DR 在 WebView
上设置下载侦听器,并使用
DownloadManager
将文件下载到 外部下载目录。
Uri
对象。MimeTypeMap
来根据
mimeType
获取扩展名.构造一个
Request
对象并将目标设置为外部Downloads 目录。还要设置
User-Agent
以保持与
WebView
发送的请求的一致性,并在 下载完成时显示通知,以便用户知道它已完成。 获取
DownloadManager
开始下载。
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);
});
WRITE_EXTERNAL_STORAGE
权限才能写入
共享 Downlaods 目录,即使是通过
DownloadManager
。如果您想支持旧版本(您确实应该),请按照以下步骤操作:
在您的Manifest
仅适用于API级别。 <=28
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
在 Activity 中添加
uri, userAgent and mimeType
public class MainActivity extends AppCompatActivity {
private Uri downloadUri;
private String downloadUserAgent;
private String downloadMimeType;
...
将下载逻辑抽象为单独的函数,以便可以重用并使其使用上一步中的全局变量。
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
的权限,并且权限仅在运行时在大于 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
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
到
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)
}