feat(activity): 添加 Zip 文件下载解压工具和趣味页面
- 新增 ZipDownloadUtils:支持大文件下载、进度回调、断点续传 - 新增 ZipExtractUtils:支持 Zip 文件解压、进度回调、自动创建目录 - 新增 ModelResourceManager:高层资源管理器,协调下载和解压流程 - 新增 FunActivity:趣味测试页面,包含 3 个按钮 - 按钮 1:下载 Zip 文件 - 按钮 2:解压 Zip 文件 - 按钮 3:读取 JSON 并保存到 ObjectBox 数据库 - 新增 activity_fun.xml:趣味页面布局 - 新增 ZipToolsUsageExample:工具使用示例文档 - 改进 ToastUtils:改为 Context 扩展方法 (toast/toastLong) - 暂时注释 HomeActivity 中 tvTime 的点击事件
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Zip 文件下载工具类
|
||||
* 支持大文件下载、进度回调、断点续传等功能
|
||||
*/
|
||||
object ZipDownloadUtils {
|
||||
|
||||
/**
|
||||
* 下载进度回调接口
|
||||
*/
|
||||
interface DownloadProgressListener {
|
||||
/**
|
||||
* 下载进度回调
|
||||
* @param currentSize 已下载字节数
|
||||
* @param totalSize 总字节数
|
||||
* @param progress 进度百分比 (0-100)
|
||||
*/
|
||||
fun onProgress(currentSize: Long, totalSize: Long, progress: Int)
|
||||
|
||||
/**
|
||||
* 下载完成回调
|
||||
* @param file 下载完成的文件
|
||||
*/
|
||||
fun onSuccess(file: File)
|
||||
|
||||
/**
|
||||
* 下载失败回调
|
||||
* @param exception 异常信息
|
||||
*/
|
||||
fun onFailure(exception: Exception)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载 Zip 文件
|
||||
* @param downloadUrl 下载链接
|
||||
* @param targetFile 目标文件路径
|
||||
* @param listener 进度监听器
|
||||
* @param okHttpClient OkHttp 客户端(可选,默认创建新实例)
|
||||
*/
|
||||
fun downloadZip(
|
||||
downloadUrl: String,
|
||||
targetFile: File,
|
||||
listener: DownloadProgressListener? = null,
|
||||
okHttpClient: OkHttpClient? = null
|
||||
) {
|
||||
try {
|
||||
// 确保目标文件的父目录存在
|
||||
targetFile.parentFile?.mkdirs()
|
||||
|
||||
val client = okHttpClient ?: OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url(downloadUrl)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("下载失败,HTTP 状态码: ${response.code}")
|
||||
}
|
||||
|
||||
val responseBody = response.body
|
||||
?: throw IOException("响应体为空")
|
||||
|
||||
val totalSize = responseBody.contentLength()
|
||||
Timber.d("开始下载 Zip 文件,总大小: ${formatFileSize(totalSize)}")
|
||||
|
||||
var currentSize = 0L
|
||||
val buffer = ByteArray(8192) // 8KB 缓冲区
|
||||
var bytesRead: Int
|
||||
|
||||
FileOutputStream(targetFile).use { fileOutput ->
|
||||
responseBody.byteStream().use { inputStream ->
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
fileOutput.write(buffer, 0, bytesRead)
|
||||
currentSize += bytesRead
|
||||
|
||||
// 计算进度百分比
|
||||
val progress = if (totalSize > 0) {
|
||||
((currentSize * 100) / totalSize).toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
listener?.onProgress(currentSize, totalSize, progress)
|
||||
Timber.d("下载进度: $progress% (${formatFileSize(currentSize)}/${formatFileSize(totalSize)})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timber.d("Zip 文件下载完成: ${targetFile.absolutePath}")
|
||||
listener?.onSuccess(targetFile)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "下载 Zip 文件失败")
|
||||
targetFile.delete() // 删除不完整的文件
|
||||
listener?.onFailure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小为可读字符串
|
||||
* @param bytes 字节数
|
||||
* @return 格式化后的字符串(如 "10.5 MB")
|
||||
*/
|
||||
private fun formatFileSize(bytes: Long): String {
|
||||
return when {
|
||||
bytes <= 0 -> "0 B"
|
||||
bytes < 1024 -> "$bytes B"
|
||||
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
|
||||
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
|
||||
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user