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,
destination: FileDownloadDestination = FileDownloadDestination.Sandbox,
): 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)
android.util.Log.d("XWV", "saveBlobDownload: decoded ${bytes.size} bytes")
val baseDir = when (destination) {
FileDownloadDestination.PublicDownloads -> {
val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
@ -260,14 +262,21 @@ object FileSDK {
android.os.Environment.DIRECTORY_DOWNLOADS
),
appName,
).apply { mkdirs() }
).apply {
val created = mkdirs()
android.util.Log.d("XWV", "saveBlobDownload: PublicDownloads dir=$absolutePath, created=$created, exists=${exists()}")
}
}
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")
android.util.Log.d("XWV", "saveBlobDownload: writing to ${target.absolutePath}")
target.writeBytes(bytes)
android.util.Log.d("XWV", "saveBlobDownload: done, size=${target.length()}")
return target
}

查看文件

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