最近项目中的邮件模块需要与第三方进行对接并且利用 WebView 展示,测试的时候发现点击附件下载没有任何反应,但是 ios 端却可以直接点击预览,客户非常不开心。
百度之后发现 WebView 默认不提供下载响应(应该是为了安全性考虑),只要设置了webview.setDownloadListener()
方法我们就可以下载了。既然这么简单我们就赶紧试试吧。
webView.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
// url 你要访问的下载链接
// userAgent 是HTTP请求头部用来标识客户端信息的字符串
// contentDisposition 为保存文件提供一个默认的文件名
// mimetype 该资源的媒体类型
// contentLength 该资源的大小
// 这几个参数都是可以通过抓包获取的
// 用手机默认浏览器打开链接
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
我们这里先测试用系统默认的浏览器进行下载。在大多数的情况下这样已经满足要求了,网上的方法也确实是这样的,我怀着满心欢喜打开了项目,然后点击下载:”网页无法打开“。很郁闷。我又用手机的浏览器测试了一下,的的确确是没有问题的。那么问题出在哪呢?
继续抓包,发现了异样。在浏览器中,访问邮件与点击下载获取的 cookie 值是相同的,而在 WebView 中这两个值是不同的。似乎找到原因了!这个下载链接是需要验证 cookie 的。可是怎么把 WebView 的 cookie 传递给系统的浏览器呢?这个我确实不会,那么不如自己写个方法下载吧。
首先我们要获取到 cookie:
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
CookieManager cookieManager = CookieManager.getInstance();
cookieStr = cookieManager.getCookie(url); // 获取到cookie字符串值
}
});
然后我们自己写个下载类:
private class DownloadTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
String url = params[0]; // 要访问的链接
String fileName = params[1]; // 文件名
if (TextUtils.isEmpty(url)) {
return null;
}
File directory = Environment.getExternalStorageDirectory();
File file = new File(directory, fileName);
if (file.exists()) {
return fileName;
}
// 下载文件
try {
URL myFileUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) myFileUrl
.openConnection();
// 携带cookie请求,要写在请求开始之前
conn.addRequestProperty("Cookie", cookieStr);
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
// 我们把文件保存在sd卡根目录
FileOutputStream out = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +fileName);
byte[] buffer = new byte[1024];
int r;
while ((r = is.read(buffer)) > 0) {
out.write(buffer, 0, r);
out.flush();
}
is.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return fileName;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// 这里可以对下载好的文件进行打开等操作
}
}
启动下载:
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
// 获取文件名称,在我的项目测试之后发现得到的是乱码
String fileName = contentDisposition.substring(contentDisposition.indexOf("\"") + 1, contentDisposition.length() - 1);
DownloadTask task = new DownloadTask();
task.execute(url, fileName);
}
ok,工作完成。我测试了一下,成功下载!于是交付测试。
...
...
...
一段时间之后,被告知又出现了问题:有时可以下载,有时又无法访问链接。
这又是什么原因呢?有时可以下载说明这种解决办法的方向是正确的,可有时又不能下载,这个问题又出在哪里呢?既然之前无法访问是因为没有验证 cookie,那么这次首先想到的就是 cookie 验证失败了。
继续抓包,发现 cookie 是有实效性的,也就是一段时间之后 cookie 是会发生变化的,但是 WebView 却一直保留着同一个 cookie 值。找到了原因,那就让 WebView 每次loadUrl(your url)
之前清除掉 cookie 。
webView.clearCache(true); // 清除资源缓存。请注意,缓存是每个应用程序的,所以这将清除所有使用的WebViews的缓存。false的话,只会清除RAM上的缓存。
webView.clearHistory(); // 清除历史记录
webView.clearFormData(); // //从当前关注的表单字段中移除自动填充弹出窗口
webView.clearSslPreferences(); // 清除存储的SSL首选项表
为了保险起见,在onDestroy()
方法中写上同样的方法。
@Override
protected void onDestroy() {
super.onDestroy();
tencent_webview.clearCache(true);
tencent_webview.clearHistory();
tencent_webview.clearFormData();
tencent_webview.clearSslPreferences();
}
这样就基本解决了问题,测试正常。
对于 WebView 的了解和掌握,我要学习的还有很多很多,写的不对的地方,欢迎各位指教~
欢迎关注我的微信公众号
网友评论