文件下载提交

This commit is contained in:
frank wang 2025-10-13 17:40:46 +08:00
parent 14587342cf
commit 04b4e489e4
2 changed files with 67 additions and 88 deletions

View File

@ -2,8 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Android 10+ 不需要 WRITE_EXTERNAL_STORAGEDownloadManager 可访问 Downloads 目录 -->
<application <application
android:name=".MyApplication" android:name=".MyApplication"

View File

@ -1,31 +1,32 @@
package com.pxkj.jwzs; package com.pxkj.jwzs;
import android.app.DownloadManager;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.net.http.SslError; import android.net.http.SslError;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.webkit.JavascriptInterface; import android.webkit.JavascriptInterface;
import android.webkit.SslErrorHandler; import android.webkit.SslErrorHandler;
import android.webkit.URLUtil;
import android.webkit.WebResourceRequest; import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse; import android.webkit.WebResourceResponse;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.widget.Toolbar; import android.widget.Toast;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.net.URLConnection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -38,7 +39,7 @@ public class FirstActivity extends BaseActivity {
private static final Map<String, String> MIME_TYPES = new HashMap<>(); private static final Map<String, String> MIME_TYPES = new HashMap<>();
static { static {
MIME_TYPES.put("js", "text/javascript"); MIME_TYPES.put("js", "text/javascript");
MIME_TYPES.put("mjs", "text/javascript"); // 模块JavaScript MIME_TYPES.put("mjs", "text/javascript");
MIME_TYPES.put("css", "text/css"); MIME_TYPES.put("css", "text/css");
MIME_TYPES.put("html", "text/html"); MIME_TYPES.put("html", "text/html");
MIME_TYPES.put("png", "image/png"); MIME_TYPES.put("png", "image/png");
@ -58,31 +59,11 @@ public class FirstActivity extends BaseActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
// 初始化Toolbar
// androidx.appcompat.widget.Toolbar toolbar = findViewById(R.id.toolbar);
// setSupportActionBar(toolbar); // 关键将Toolbar设为ActionBar
// 显示返回按钮
// if (getSupportActionBar() != null) {
// getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// getSupportActionBar().setDisplayShowHomeEnabled(true);
// }
// 设置返回按钮点击事件
// toolbar.setNavigationOnClickListener(v -> {
// if (webView.canGoBack()) {
// webView.goBack();
// } else {
// finish();
// }
// });
initWebView(); initWebView();
loadLocalH5(); loadLocalH5();
verifyAssets(); verifyAssets();
} }
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (webView.canGoBack()) { if (webView.canGoBack()) {
@ -91,26 +72,11 @@ public class FirstActivity extends BaseActivity {
super.onBackPressed(); super.onBackPressed();
} }
} }
private void verifyAssets() { private void verifyAssets() {
try { try {
String[] files = getAssets().list("web"); String[] files = getAssets().list("web");
Log.d(TAG, "Assets/web 目录内容: " + java.util.Arrays.toString(files)); Log.d(TAG, "Assets/web 目录内容: " + java.util.Arrays.toString(files));
if (files != null) {
boolean hasIndexHtml = false;
boolean hasAssetsDir = false;
for (String file : files) {
if (file.equals("index.html")) hasIndexHtml = true;
if (file.equals("assets")) hasAssetsDir = true;
}
Log.d(TAG, "index.html 存在: " + hasIndexHtml);
Log.d(TAG, "assets 目录存在: " + hasAssetsDir);
if (hasAssetsDir) {
String[] assetFiles = getAssets().list("web/assets");
Log.d(TAG, "Assets/web/assets 目录内容: " + java.util.Arrays.toString(assetFiles));
}
}
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "无法读取 assets 目录", e); Log.e(TAG, "无法读取 assets 目录", e);
} }
@ -120,7 +86,6 @@ public class FirstActivity extends BaseActivity {
webView = findViewById(R.id.webview); webView = findViewById(R.id.webview);
WebSettings settings = webView.getSettings(); WebSettings settings = webView.getSettings();
// 基本设置
settings.setJavaScriptEnabled(true); settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true); settings.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true); settings.setDatabaseEnabled(true);
@ -130,30 +95,21 @@ public class FirstActivity extends BaseActivity {
settings.setLoadsImagesAutomatically(true); settings.setLoadsImagesAutomatically(true);
settings.setBlockNetworkImage(false); settings.setBlockNetworkImage(false);
settings.setBlockNetworkLoads(false); settings.setBlockNetworkLoads(false);
// 缓存设置
settings.setCacheMode(WebSettings.LOAD_DEFAULT); settings.setCacheMode(WebSettings.LOAD_DEFAULT);
// 跨域设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
settings.setAllowFileAccessFromFileURLs(true); settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true); settings.setAllowUniversalAccessFromFileURLs(true);
} }
// 混合内容设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
} }
// 对于Android 10+的暗黑模式设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
settings.setForceDark(WebSettings.FORCE_DARK_OFF); settings.setForceDark(WebSettings.FORCE_DARK_OFF);
} }
// 添加JS接口
webView.addJavascriptInterface(new JsBridge(this), "AndroidBridge"); webView.addJavascriptInterface(new JsBridge(this), "AndroidBridge");
// 启用WebView调试
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true); WebView.setWebContentsDebuggingEnabled(true);
} }
@ -168,16 +124,23 @@ public class FirstActivity extends BaseActivity {
@Override @Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// 忽略SSL错误仅用于开发环境
handler.proceed(); handler.proceed();
} }
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (isDownloadUrl(url)) {
downloadFile(url);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override @Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String url = request.getUrl().toString(); String url = request.getUrl().toString();
log(TAG, "拦截请求: " + url); log(TAG, "拦截请求: " + url);
return handleResourceRequest(url); return handleResourceRequest(url);
} }
@ -194,52 +157,37 @@ public class FirstActivity extends BaseActivity {
if (url.startsWith("file:///android_asset/")) { if (url.startsWith("file:///android_asset/")) {
try { try {
String assetPath = url.replace("file:///android_asset/", ""); String assetPath = url.replace("file:///android_asset/", "");
assetPath = assetPath.replace("web/./", "web/").replace("/./", "/");
// 标准化路径处理 if (!assetPath.startsWith("web/")) assetPath = "web/" + assetPath;
assetPath = assetPath.replace("web/./", "web/")
.replace("/./", "/");
// 确保路径以web/开头
if (!assetPath.startsWith("web/")) {
assetPath = "web/" + assetPath;
}
InputStream inputStream = getAssets().open(assetPath); InputStream inputStream = getAssets().open(assetPath);
String mimeType = getMimeTypeForUrl(url); String mimeType = getMimeTypeForUrl(url);
log(TAG, "成功加载资源: " + assetPath + " | MIME: " + mimeType); log(TAG, "成功加载资源: " + assetPath + " | MIME: " + mimeType);
return new WebResourceResponse(mimeType, "UTF-8", inputStream); return new WebResourceResponse(mimeType, "UTF-8", inputStream);
} catch (IOException e) { } catch (IOException e) {
log(TAG, "资源加载失败: " + url + ", 错误: " + e.getMessage()); log(TAG, "资源加载失败: " + url + ", 错误: " + e.getMessage());
return null; // 返回null让WebView尝试其他方式加载 return null;
} }
} }
return null; return null;
} }
}); });
// DownloadListener 备用
webView.setDownloadListener((url, userAgent, contentDisposition, mimeType, contentLength) -> {
downloadFile(url);
});
} }
private String getMimeTypeForUrl(String url) { private String getMimeTypeForUrl(String url) {
// 特殊处理模块脚本 if (url.contains("index-DJSXJc-t.js") || url.contains(".mjs") || (url.contains("module") && url.endsWith(".js"))) {
if (url.contains("index-DJSXJc-t.js") || url.contains(".mjs") ||
(url.contains("module") && url.endsWith(".js"))) {
return "text/javascript"; return "text/javascript";
} }
// 移除URL中的查询参数和哈希
String cleanUrl = url.split("[?#]")[0]; String cleanUrl = url.split("[?#]")[0];
// 从文件扩展名获取MIME类型
String extension = ""; String extension = "";
int dotIndex = cleanUrl.lastIndexOf('.'); int dotIndex = cleanUrl.lastIndexOf('.');
if (dotIndex > 0) { if (dotIndex > 0) extension = cleanUrl.substring(dotIndex + 1).toLowerCase();
extension = cleanUrl.substring(dotIndex + 1).toLowerCase();
}
// 从预设映射中获取MIME类型
String mimeType = MIME_TYPES.get(extension); String mimeType = MIME_TYPES.get(extension);
// 默认值
return mimeType != null ? mimeType : "text/plain"; return mimeType != null ? mimeType : "text/plain";
} }
@ -251,13 +199,46 @@ public class FirstActivity extends BaseActivity {
private void loadLocalH5() { private void loadLocalH5() {
runOnUiThread(() -> { runOnUiThread(() -> {
// 直接使用loadUrl加载确保路径解析正确
String localUrl = "file:///android_asset/web/index.html"; String localUrl = "file:///android_asset/web/index.html";
log(TAG, "使用 loadUrl 加载: " + localUrl); log(TAG, "使用 loadUrl 加载: " + localUrl);
webView.loadUrl(localUrl); webView.loadUrl(localUrl);
}); });
} }
private boolean isDownloadUrl(String url) {
if (TextUtils.isEmpty(url)) return false;
return true;
// return url.endsWith(".pdf")
// || url.endsWith(".doc")
// || url.endsWith(".docx")
// || url.endsWith(".xls")
// || url.endsWith(".xlsx")
// || url.contains("/api/file/");
}
private void downloadFile(String url) {
try {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
String fileName = URLUtil.guessFileName(url, null, null);
request.setTitle(fileName);
request.setDescription("正在下载文件...");
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
request.allowScanningByMediaScanner();
request.addRequestHeader("User-Agent", System.getProperty("http.agent"));
DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
dm.enqueue(request);
Toast.makeText(this, "文件已开始下载,完成后可在通知栏查看", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "下载失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public static class JsBridge { public static class JsBridge {
private final WeakReference<FirstActivity> activityRef; private final WeakReference<FirstActivity> activityRef;
@ -271,8 +252,7 @@ public class FirstActivity extends BaseActivity {
if (activity != null) { if (activity != null) {
showLog("[Android] 开始获取资源地址..."); showLog("[Android] 开始获取资源地址...");
String baseUrl = SpUtils.getString(activity, "TARGET_URL", ""); String baseUrl = SpUtils.getString(activity, "TARGET_URL", "");
String logMsg = "[Android] 获取到资源地址: " + baseUrl; showLog("[Android] 获取到资源地址: " + baseUrl);
showLog(logMsg);
return baseUrl; return baseUrl;
} }
return null; return null;
@ -280,7 +260,7 @@ public class FirstActivity extends BaseActivity {
@JavascriptInterface @JavascriptInterface
public void callApiWithAddress(String apiPath) { public void callApiWithAddress(String apiPath) {
// 实现保持不变 // 保持空实现
} }
@JavascriptInterface @JavascriptInterface
@ -288,9 +268,7 @@ public class FirstActivity extends BaseActivity {
FirstActivity activity = activityRef.get(); FirstActivity activity = activityRef.get();
if (activity != null) { if (activity != null) {
activity.runOnUiThread(() -> { activity.runOnUiThread(() -> {
// 安全转义 String escapedMessage = message.replace("\\", "\\\\")
String escapedMessage = message
.replace("\\", "\\\\")
.replace("'", "\\'") .replace("'", "\\'")
.replace("\"", "\\\"") .replace("\"", "\\\"")
.replace("\n", "\\n") .replace("\n", "\\n")
@ -307,7 +285,7 @@ public class FirstActivity extends BaseActivity {
protected void refresh() { protected void refresh() {
// 空实现 // 空实现
} }
//
// private void log(String tag, String message) { // private void log(String tag, String message) {
// Log.d(tag, message); // Log.d(tag, message);
// if (webViewLogger != null) { // if (webViewLogger != null) {
@ -330,4 +308,4 @@ class WebViewLogger {
Log.d(tag, message); Log.d(tag, message);
handler.post(() -> bridge.showLog("[" + tag + "] " + message)); handler.post(() -> bridge.showLog("[" + tag + "] " + message));
} }
} }