- 新增 ZipDownloadUtils:支持大文件下载、进度回调、断点续传 - 新增 ZipExtractUtils:支持 Zip 文件解压、进度回调、自动创建目录 - 新增 ModelResourceManager:高层资源管理器,协调下载和解压流程 - 新增 FunActivity:趣味测试页面,包含 3 个按钮 - 按钮 1:下载 Zip 文件 - 按钮 2:解压 Zip 文件 - 按钮 3:读取 JSON 并保存到 ObjectBox 数据库 - 新增 activity_fun.xml:趣味页面布局 - 新增 ZipToolsUsageExample:工具使用示例文档 - 改进 ToastUtils:改为 Context 扩展方法 (toast/toastLong) - 暂时注释 HomeActivity 中 tvTime 的点击事件
123 lines
3.9 KiB
Kotlin
123 lines
3.9 KiB
Kotlin
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))
|
||
}
|
||
}
|
||
}
|