Android DownloadManager从WebView下载MP4文件后缀自动变为.bin问题
Android DownloadManager 下载MP4文件后缀变为.bin的解决方案
根因说明
- 你代码中
request.setMimeType(mimetype)传入的是服务器返回的application/octet-stream,并未真正覆盖为正确的video/mp4类型 - 旧版本Android系统的DownloadManager会优先读取服务器响应的Content-Type,当类型为通用二进制流
application/octet-stream时,即使你传入的文件名带.mp4后缀,系统也会强制修正后缀为.bin URLUtil.guessFileName方法受传入的MIME类型影响,当传入类型为application/octet-stream时,部分场景下会自动将后缀替换为.bin,即使Content-Disposition头中存在正确的.mp4文件名
修复步骤
1. 手动提取并校验文件名
优先从Content-Disposition头中提取文件名,避免依赖系统的guess逻辑,同时强制校验文件名后缀为.mp4
2. 强制覆盖正确的MIME类型
根据文件后缀手动指定正确的MIME类型,不要直接使用服务器返回的application/octet-stream
3. 正确配置WebView的下载监听
WebView的下载事件需要通过DownloadListener接管,不要仅依赖shouldOverrideUrlLoading处理跳转逻辑
修复后的代码示例
DownloadManager 下载逻辑修改
// 从contentDisposition中提取真实文件名 String fileName = ""; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { ContentDisposition contentDispositionObj = ContentDisposition.parse(contentDisposition); fileName = contentDispositionObj.getFilename(); } catch (IllegalArgumentException e) { e.printStackTrace(); } } else { // 低版本系统正则匹配文件名 Pattern pattern = Pattern.compile("filename=\"(.*?)\""); Matcher matcher = pattern.matcher(contentDisposition); if (matcher.find()) { fileName = matcher.group(1); } } // 提取失败时fallback到系统方法 if (TextUtils.isEmpty(fileName)) { fileName = URLUtil.guessFileName(url, contentDisposition, mimetype); } // 强制校验文件名后缀 if (!fileName.toLowerCase().endsWith(".mp4")) { fileName += ".mp4"; } // 覆盖为正确的MIME类型 String finalMimeType = mimetype; if (fileName.toLowerCase().endsWith(".mp4")) { finalMimeType = "video/mp4"; } // 构建下载请求 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); String cookies = CookieManager.getInstance().getCookie(url); request.addRequestHeader("cookie", cookies); request.setMimeType(finalMimeType); request.addRequestHeader("User-Agent", userAgent); request.setTitle(fileName); request.setDescription("Downloading File..."); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // 传入校验后的文件名 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); request.allowScanningByMediaScanner(); DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); dm.enqueue(request);
WebView 配置修改
// 给WebView设置下载监听器,接管下载事件 webView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { // 调用上述修改后的下载逻辑即可 startDownload(url, userAgent, contentDisposition, mimetype); } });
WebViewClient 优化(可选)
原有shouldOverrideUrlLoading逻辑会接管所有页面跳转,可优化为仅处理非HTTP/HTTPS链接:
@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { Uri uri = request.getUrl(); String scheme = uri.getScheme(); if (scheme == null || (!scheme.startsWith("http") && !scheme.startsWith("https"))) { try { Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); return true; } catch (Exception e) { // 无对应处理应用时忽略 } } view.loadUrl(uri.toString()); return true; }
内容的提问来源于stack exchange,提问作者zarez
相关产品推荐
相关产品推荐

