debug(file): 添加文件下载功能的调试日志

- 在 FileSDK 的 saveBlobDownload 方法中添加解码、目录创建和文件写入的日志记录
- 在 XWebViewView 的 readBlobAndPost 函数中添加 JavaScript 端的数据读取和错误处理日志
- 添加锚点拦截和消息传递过程中的日志记录
- 在 blobdownload 处理流程中添加成功和失败的状态日志
- 增加文件保存到相册操作的结果日志记录
这个提交包含在:
XuqmGroup 2026-06-10 20:13:40 +08:00
父节点 d8c0abe510
当前提交 2bd497ead4
共有 2 个文件被更改,包括 36 次插入4 次删除

查看文件

@ -251,7 +251,9 @@ object FileSDK {
fileName: String, fileName: String,
destination: FileDownloadDestination = FileDownloadDestination.Sandbox, destination: FileDownloadDestination = FileDownloadDestination.Sandbox,
): File { ): File {
android.util.Log.d("XWV", "saveBlobDownload: fileName=$fileName, dest=$destination, b64Len=${base64Data.length}")
val bytes = android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT) val bytes = android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT)
android.util.Log.d("XWV", "saveBlobDownload: decoded ${bytes.size} bytes")
val baseDir = when (destination) { val baseDir = when (destination) {
FileDownloadDestination.PublicDownloads -> { FileDownloadDestination.PublicDownloads -> {
val appName = context.applicationInfo.loadLabel(context.packageManager).toString() val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
@ -260,14 +262,21 @@ object FileSDK {
android.os.Environment.DIRECTORY_DOWNLOADS android.os.Environment.DIRECTORY_DOWNLOADS
), ),
appName, appName,
).apply { mkdirs() } ).apply {
val created = mkdirs()
android.util.Log.d("XWV", "saveBlobDownload: PublicDownloads dir=$absolutePath, created=$created, exists=${exists()}")
}
} }
FileDownloadDestination.Sandbox -> { FileDownloadDestination.Sandbox -> {
context.getExternalFilesDir(null) ?: context.filesDir (context.getExternalFilesDir(null) ?: context.filesDir).also {
android.util.Log.d("XWV", "saveBlobDownload: Sandbox dir=${it.absolutePath}")
}
} }
} }
val target = uniqueFile(baseDir, fileName.takeIf { it.isNotBlank() } ?: "download.bin") val target = uniqueFile(baseDir, fileName.takeIf { it.isNotBlank() } ?: "download.bin")
android.util.Log.d("XWV", "saveBlobDownload: writing to ${target.absolutePath}")
target.writeBytes(bytes) target.writeBytes(bytes)
android.util.Log.d("XWV", "saveBlobDownload: done, size=${target.length()}")
return target return target
} }

查看文件

@ -100,17 +100,29 @@ internal fun buildDialogOverrideJs(bridgeName: String) = """
URL.revokeObjectURL = function() {}; URL.revokeObjectURL = function() {};
function readBlobAndPost(blobUrl, filename) { function readBlobAndPost(blobUrl, filename) {
console.log('[XWV] readBlobAndPost start, url=' + blobUrl + ', filename=' + filename);
fetch(blobUrl) fetch(blobUrl)
.then(function(r) { return r.blob(); }) .then(function(r) {
console.log('[XWV] fetch ok, size=' + r.size);
return r.blob();
})
.then(function(blob) { .then(function(blob) {
console.log('[XWV] blob ok, size=' + blob.size + ', type=' + blob.type);
var reader = new FileReader(); var reader = new FileReader();
reader.onloadend = function() { reader.onloadend = function() {
var b64 = reader.result.split(',')[1]; var b64 = reader.result.split(',')[1];
console.log('[XWV] base64 ready, len=' + (b64 ? b64.length : 0));
post({ __xwv: 'blobdownload', url: blobUrl, filename: filename, data: b64 }); post({ __xwv: 'blobdownload', url: blobUrl, filename: filename, data: b64 });
console.log('[XWV] blobdownload posted');
};
reader.onerror = function(e) {
console.log('[XWV] FileReader error: ' + String(e));
post({ __xwv: 'bloberror', msg: 'FileReader error: ' + String(e) });
}; };
reader.readAsDataURL(blob); reader.readAsDataURL(blob);
}) })
.catch(function(err) { .catch(function(err) {
console.log('[XWV] readBlobAndPost ERROR: ' + String(err));
post({ __xwv: 'bloberror', msg: String(err) }); post({ __xwv: 'bloberror', msg: String(err) });
}); });
} }
@ -125,6 +137,7 @@ internal fun buildDialogOverrideJs(bridgeName: String) = """
var hasDownloadAttr = el.hasAttribute('download'); var hasDownloadAttr = el.hasAttribute('download');
var dlName = el.getAttribute('download') || ''; var dlName = el.getAttribute('download') || '';
var isDL = hasDownloadAttr || dlRe.test(href); var isDL = hasDownloadAttr || dlRe.test(href);
console.log('[XWV] tryInterceptAnchor href=' + href + ', hasDownload=' + hasDownloadAttr + ', isDL=' + isDL);
if (isDL) { if (isDL) {
if (e) { e.preventDefault(); e.stopPropagation(); } if (e) { e.preventDefault(); e.stopPropagation(); }
if (href.startsWith('blob:')) { if (href.startsWith('blob:')) {
@ -180,9 +193,11 @@ internal class XWebViewJsBridge(
) { ) {
@JavascriptInterface @JavascriptInterface
fun postMessage(data: String) { fun postMessage(data: String) {
android.util.Log.d("XWV", "postMessage: ${data.take(200)}")
mainHandler.post { mainHandler.post {
val json = runCatching { JSONObject(data) }.getOrNull() val json = runCatching { JSONObject(data) }.getOrNull()
val xwv = json?.optString("__xwv")?.takeIf { it.isNotEmpty() } val xwv = json?.optString("__xwv")?.takeIf { it.isNotEmpty() }
android.util.Log.d("XWV", "postMessage parsed: xwv=$xwv")
if (xwv != null && json != null) { if (xwv != null && json != null) {
onXwvMessage()?.invoke(xwv, json) onXwvMessage()?.invoke(xwv, json)
} else { } else {
@ -229,6 +244,7 @@ fun XWebViewView(
// Handles __xwv: 'download' / 'blobdownload' messages from the injected JS. // Handles __xwv: 'download' / 'blobdownload' messages from the injected JS.
val xwvMessageHandler: (String, JSONObject) -> Unit = handler@{ type, payload -> val xwvMessageHandler: (String, JSONObject) -> Unit = handler@{ type, payload ->
android.util.Log.d("XWV", "xwvMessage type=$type, payload=${payload.toString().take(200)}")
when (type) { when (type) {
"download" -> { "download" -> {
val url = payload.optString("url").takeIf { it.isNotBlank() } ?: return@handler val url = payload.optString("url").takeIf { it.isNotBlank() } ?: return@handler
@ -267,19 +283,26 @@ fun XWebViewView(
"blobdownload" -> { "blobdownload" -> {
val url = payload.optString("url") val url = payload.optString("url")
val filename = payload.optString("filename").takeIf { it.isNotBlank() } ?: "download.bin" val filename = payload.optString("filename").takeIf { it.isNotBlank() } ?: "download.bin"
val b64 = payload.optString("data").takeIf { it.isNotBlank() } ?: return@handler val b64 = payload.optString("data").takeIf { it.isNotBlank() } ?: run {
android.util.Log.e("XWV", "blobdownload: empty data")
return@handler
}
android.util.Log.d("XWV", "blobdownload: filename=$filename, b64Len=${b64.length}, dest=${config.downloadDestination}")
coroutineScope.launch(Dispatchers.IO) { coroutineScope.launch(Dispatchers.IO) {
runCatching { runCatching {
FileSDK.saveBlobDownload(context, b64, filename, config.downloadDestination) FileSDK.saveBlobDownload(context, b64, filename, config.downloadDestination)
}.onSuccess { file -> }.onSuccess { file ->
android.util.Log.d("XWV", "blobdownload saved: ${file.absolutePath}, size=${file.length()}")
// For image files, save to the system gallery (相册) so they appear in Photos // For image files, save to the system gallery (相册) so they appear in Photos
val savedToGallery = runCatching { FileSDK.saveImageToGallery(context, file) }.getOrDefault(false) val savedToGallery = runCatching { FileSDK.saveImageToGallery(context, file) }.getOrDefault(false)
android.util.Log.d("XWV", "blobdownload savedToGallery=$savedToGallery")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
dispatchDownloadEvent("__xwvDownloadDone", url, ",success:true") dispatchDownloadEvent("__xwvDownloadDone", url, ",success:true")
// Only open with file viewer when not saved to gallery (e.g. zip, docx) // Only open with file viewer when not saved to gallery (e.g. zip, docx)
if (!savedToGallery) FileSDK.openFile(context, file) if (!savedToGallery) FileSDK.openFile(context, file)
} }
}.onFailure { e -> }.onFailure { e ->
android.util.Log.e("XWV", "blobdownload FAILED", e)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
dispatchDownloadEvent("__xwvDownloadDone", url, ",success:false,error:'${e.message?.escapeJs()}'") dispatchDownloadEvent("__xwvDownloadDone", url, ",success:false,error:'${e.message?.escapeJs()}'")
} }