目录下载

最近更新时间:2025-11-26 17:46:01

我的收藏

简介

本文档提供关于目录下载的接口说明以及 SDK 示例代码,支持递归下载整个目录树,包括所有子目录和文件。该功能具有以下特点:
递归下载:自动遍历并下载目录中的所有子目录和文件。
任务管理:支持启动、暂停、恢复、取消等完整的任务生命周期管理。
进度跟踪:实时监控下载进度和状态变化。
冲突处理:支持多种文件冲突策略(询问、重命名、覆盖)。
数据持久化:任务信息自动保存到数据库,支持应用重启后恢复。
查询过滤:支持按状态、类型、排序等多种条件查询任务。
注意:
协程环境:所有 API 都是挂起函数,需要在协程环境中调用。
回调线程:回调可能在后台线程执行,更新 UI 时需要切换到主线程。
任务持久化:任务信息自动保存到数据库,应用重启后可通过 restoreRunningTasks() 恢复。
错误处理:下载失败时,可通过 errorTypeerrorCodeerrorInfo 获取详细错误信息。
冲突策略:建议使用 RENAME 策略避免文件覆盖。

创建任务

通过 SMHCollection 的 downloadDirectory 方法创建一个新的目录下载任务。
suspend fun downloadDirectory(
dirPath: String, // 云端目录路径
localPath: String, // 本地保存路径
conflictStrategy: ConflictStrategy = ConflictStrategy.RENAME, // 冲突策略
crc64Check: Boolean = true // 是否开启 CRC64 校验
): SMHDownloadDirTask

参数说明

参数名称
描述
类型
dirPath
云端目录路径,例如 "/documents/project"
String
localPath
本地保存路径,例如 "/sdcard/Download/project"
String
conflictStrategy
文件冲突时的处理策略,取值如下:
ASK:冲突时抛出异常
RENAME:自动重命名(默认)
OVERWRITE:覆盖已存在的文件
ConflictStrategy

crc64Check
是否开启 CRC64完整性校验,默认为 true
Boolean

返回结果说明

返回 SMHDownloadDirTask 对象,代表目录下载任务。

示例代码

// 创建目录下载任务
val task = smhCollection.downloadDirectory(
dirPath = "/documents/project",
localPath = "/sdcard/Download/project",
conflictStrategy = ConflictStrategy.RENAME,
crc64Check = true
)

// 设置回调
task.onStateChange = { path, status, errorType, errorCode, errorMessage ->
Log.i(TAG, "状态变化: $path -> $status")
if (status == DownloadDirState.FAILURE) {
Log.e(TAG, "下载失败: $errorMessage")
}
}

task.onProgressChange = { path, count, total ->
val progress = if (total > 0) (count * 100 / total) else 0
Log.i(TAG, "下载进度: $path -> $progress% ($count/$total)")
}

task.onContentsReceived = { path, contents, lastPage ->
Log.i(TAG, "收到目录内容: $path, 文件数: ${contents.size}, 最后一页: $lastPage")
}

// 启动任务
task.start()

管理任务

查询任务

获取单个任务

通过 SMHCollection 的 getDownloadDirTask 方法根据路径获取指定的目录或文件下载任务。
suspend fun getDownloadDirTask(
path: String, // 云端路径
localPath: String // 本地路径
): SMHDownloadDirTaskBase?
参数说明
参数名称
描述
类型
path
云端目录路径,例如 "/documents/project"
String
localPath
本地保存路径,例如 "/sdcard/Download/project"
String
返回结果说明
返回 SMHDownloadDirTask(目录任务)或 SMHDownloadDirFileTask(文件任务)
如果任务不存在,返回 null
示例代码
// 获取指定任务
val task = smhCollection.getDownloadDirTask(
path = "/documents/project",
localPath = "/sdcard/Download/project"
)

if (task != null) {
Log.i(TAG, "任务状态: ${task.status}")
Log.i(TAG, "下载进度: ${task.count}/${task.total}")
}

查询任务列表

通过 SMHCollection 的 getDownloadDirTasks 方法查询指定目录下的子任务列表,支持分页、排序、过滤等功能。
方法1:使用参数方式
suspend fun getDownloadDirTasks(
dirPath: String, // 父目录路径
localPath: String, // 父目录本地路径
page: Int? = null, // 页码(从1开始)
pageSize: Int? = null, // 每页大小
orderType: DownloadDirOrderType = DownloadDirOrderType.CREATE_TIME, // 排序字段
orderDirection: OrderDirection = OrderDirection.ASC, // 排序方向
sortType: DownloadDirSortType = DownloadDirSortType.SEPARATE, // 排序类型
directoryFilter: DirectoryFilter? = null, // 类型过滤
status: DownloadDirState? = null // 状态过滤
): List<SMHDownloadDirTaskBase>
方法2:使用 Builder 模式
suspend fun getDownloadDirTasks(
params: DownloadDirQueryParams
): List<SMHDownloadDirTaskBase>
参数说明
参数名称
描述
类型
dirPath
父目录的云端路径
String
localPath
父目录的本地路径
String
page
页码,从1开始,为 null 时返回全部
Int
pageSize
每页大小,为 null 时返回全部
Int
orderType
排序字段,取值如下:
PATH:按路径排序
COUNT:按数量/大小排序
CREATE_TIME:按创建时间排序(默认)
UPDATE_TIME:按更新时间排序
DownloadDirOrderType
orderDirection
排序方向,取值如下:
ASC:升序(默认)
DESC:降序
OrderDirection

sortType
排序类型,取值如下:
SEPARATE:文件夹和文件分开排序,先文件夹后文件(默认)
UNION:文件夹和文件统一排序
DownloadDirSortType

directoryFilter
类型过滤,取值如下:
null:返回全部(默认)
DirectoryFilter.ONLY_FILE:仅返回文件
DirectoryFilter.ONLY_DIRECTORY:仅返回目录
DirectoryFilter?

status
状态过滤,取值如下:
null:返回所有状态(默认)
WAITING:等待中
RUNNING:运行中
PAUSED:已暂停
COMPLETE:已完成
FAILURE:失败
DownloadDirState?

返回结果说明
返回 SMHDownloadDirTask(目录任务)或 SMHDownloadDirFileTask(文件任务)的集合。
示例代码
// 方式1:使用参数方式
val tasks = smhCollection.getDownloadDirTasks(
dirPath = "/documents/project",
localPath = "/sdcard/Download/project",
page = 1,
pageSize = 20,
orderType = DownloadDirOrderType.CREATE_TIME,
orderDirection = OrderDirection.DESC,
directoryFilter = DirectoryFilter.ONLY_FILE,
status = DownloadDirState.RUNNING
)

// 方式2:使用 Builder 模式
val tasks = smhCollection.getDownloadDirTasks(
DownloadDirQueryParams.builder("/documents/project", "/sdcard/Download/project")
.page(1)
.pageSize(20)
.orderType(DownloadDirOrderType.CREATE_TIME)
.orderDirection(OrderDirection.DESC)
.directoryFilter(DirectoryFilter.ONLY_FILE)
.status(DownloadDirState.RUNNING)
.build()
)

// 遍历任务
tasks.forEach { task ->
when (task) {
is SMHDownloadDirTask -> {
Log.i(TAG, "目录任务: ${task.path}, 状态: ${task.status}")
}
is SMHDownloadDirFileTask -> {
Log.i(TAG, "文件任务: ${task.path}, 状态: ${task.status}")
}
}
}

统计任务数量

通过 SMHCollection 的 countDownloadDirTasks 方法统计符合条件的任务数量。
suspend fun countDownloadDirTasks(
dirPath: String, // 父目录路径
localPath: String, // 父目录本地路径
directoryFilter: DirectoryFilter? = null, // 类型过滤
status: DownloadDirState? = null // 状态过滤
): Int

参数说明

参数名称
描述
类型
dirPath
父目录的云端路径
String
localPath
父目录的本地路径
String
directoryFilter
类型过滤,取值如下:
null:返回全部(默认)
DirectoryFilter.ONLY_FILE:仅返回文件
DirectoryFilter.ONLY_DIRECTORY:仅返回目录
DirectoryFilter?
status
状态过滤,取值如下:
null:返回所有状态(默认)
WAITING:等待中
RUNNING:运行中
PAUSED:已暂停
COMPLETE:已完成
FAILURE:失败
DownloadDirState?

返回结果说明

返回符合统计条件的任务数量。

示例代码

// 统计所有任务
val totalCount = smhCollection.countDownloadDirTasks(
dirPath = "/documents/project",
localPath = "/sdcard/Download/project"
)

// 统计运行中的文件任务
val runningFileCount = smhCollection.countDownloadDirTasks(
dirPath = "/documents/project",
localPath = "/sdcard/Download/project",
directoryFilter = DirectoryFilter.ONLY_FILE,
status = DownloadDirState.RUNNING
)

Log.i(TAG, "总任务数: $totalCount, 运行中的文件: $runningFileCount")

查看任务类型

目录任务 SMHDownloadDirTask

代表一个目录下载任务,负责遍历目录内容并创建子任务。
属性
// 基础属性(继承自 SMHDownloadDirTaskBase)
val libraryId: String // 资源库 ID
val spaceId: String? // 空间 ID
val userId: String? // 用户 ID
val path: String // 云端路径
val localPath: String // 本地路径
val realLocalPath: String // 真实本地路径(重命名后)
val conflictStrategy: ConflictStrategy // 冲突策略
val crc64Check: Boolean // 是否开启 CRC64 校验
val createTime: Long // 创建时间

// 状态属性
var status: DownloadDirState // 任务状态
var count: Long // 已完成的子任务数
var total: Long // 总子任务数
var errorType: String? // 错误类型
var errorCode: String? // 错误码
var errorInfo: String? // 错误信息

// 目录任务特有属性
var nextMarker: String? // 分页标记
回调
// 状态变更回调
var onStateChange: ((path: String, status: DownloadDirState,
errorType: String?, errorCode: String?, errorMessage: String?) -> Unit)?

// 进度变更回调
var onProgressChange: ((path: String, count: Long, total: Long) -> Unit)?

// 目录内容回调(仅当前目录,不包含子孙目录)
var onContentsReceived: ((path: String, contents: List<MediaContent>, lastPage: Boolean) -> Unit)?
方法
suspend fun start() // 启动任务
suspend fun pause() // 暂停任务
suspend fun resume() // 恢复任务
suspend fun cancel() // 取消任务
suspend fun delete() // 删除任务

文件任务 SMHDownloadDirFileTask

代表一个文件下载任务,由目录任务自动创建。
属性
// 基础属性(继承自 SMHDownloadDirTaskBase)
val libraryId: String // 资源库 ID
val spaceId: String? // 空间 ID
val userId: String? // 用户 ID
val path: String // 云端路径
val localPath: String // 本地路径
val realLocalPath: String // 真实本地路径(重命名后)
val conflictStrategy: ConflictStrategy // 冲突策略
val crc64Check: Boolean // 是否开启 CRC64 校验
val createTime: Long // 创建时间

// 状态属性
var status: DownloadDirState // 任务状态
var count: Long // 已下载字节数
var total: Long // 文件总大小
var errorType: String? // 错误类型
var errorCode: String? // 错误码
var errorInfo: String? // 错误信息
回调
// 状态变更回调
var onStateChange: ((path: String, status: DownloadDirState,
errorType: String?, errorCode: String?, errorMessage: String?) -> Unit)?

// 进度变更回调(实时更新下载字节数)
var onProgressChange: ((path: String, count: Long, total: Long) -> Unit)?
方法
suspend fun start() // 启动任务(加入调度队列)
suspend fun pause() // 暂停任务
suspend fun resume() // 恢复任务
suspend fun cancel() // 取消任务
suspend fun delete() // 删除任务

查看任务状态

注意:取消的任务状态为 FAILURE,错误码为 ManualCanceled。
enum class DownloadDirState {
WAITING, // 等待中(等待调度器调度)
RUNNING, // 运行中
PAUSED, // 已暂停
COMPLETE, // 已完成
FAILURE // 失败
}

任务重启

应用重启后,通过 SMHCollection 的 restoreRunningTasks 方法恢复数据库中所有运行中的任务。通常在应用启动时调用。
如果不需要全部重启,也可以通过 API 获取相应的 Task,根据业务场景进行启动。
suspend fun restoreRunningTasks()

使用示例

示例1:基本下载流程

class DownloadActivity : AppCompatActivity() {
private lateinit var smhCollection: SMHCollection
private var downloadTask: SMHDownloadDirTask? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// 初始化SMHCollection
smhCollection = SMHCollection(
context = this,
user = MySMHUser(),
customHost = "<libraryId>.api.tencentsmh.cn"
)

// 开始下载
lifecycleScope.launch {
startDownload()
}
}

private suspend fun startDownload() {
// 创建下载任务
downloadTask = smhCollection.downloadDirectory(
dirPath = "/documents/project",
localPath = "${getExternalFilesDir(null)}/project",
conflictStrategy = ConflictStrategy.RENAME
)

// 设置状态回调
downloadTask.onStateChange = { path, status, errorType, errorCode, errorMessage ->
lifecycleScope.launch(Dispatchers.Main) {
when (status) {
DownloadDirState.RUNNING -> {
Log.i(TAG, "开始下载: $path")
}
DownloadDirState.COMPLETE -> {
Log.i(TAG, "下载完成: $path")
Toast.makeText(this@DownloadActivity, "下载完成", Toast.LENGTH_SHORT).show()
}
DownloadDirState.FAILURE -> {
Log.e(TAG, "下载失败: $path, $errorMessage")
Toast.makeText(this@DownloadActivity, "下载失败: $errorMessage", Toast.LENGTH_SHORT).show()
}
else -> {}
}
}
}

// 设置进度回调
downloadTask.onProgressChange = { path, count, total ->
val progress = if (total > 0) (count * 100 / total).toInt() else 0
lifecycleScope.launch(Dispatchers.Main) {
progressBar.progress = progress
tvProgress.text = "$progress% ($count/$total)"
}
}

// 设置内容回调
downloadTask.onContentsReceived = { path, contents, lastPage ->
Log.i(TAG, "收到目录内容: $path")
contents.forEach { content ->
Log.i(TAG, " - ${content.name} (${content.type})")
}
}

// 启动任务
downloadTask.start()
}

// 暂停下载
fun pauseDownload() {
lifecycleScope.launch {
downloadTask.pause()
}
}

// 恢复下载
fun resumeDownload() {
lifecycleScope.launch {
downloadTask.resume()
}
}

// 取消下载
fun cancelDownload() {
lifecycleScope.launch {
downloadTask.cancel()
}
}
}

示例2:查询和管理任务

class TaskManagerActivity : AppCompatActivity() {
private lateinit var smhCollection: SMHCollection

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

lifecycleScope.launch {
loadTasks()
}
}

private suspend fun loadTasks() {
// 查询所有运行中的任务
val runningTasks = smhCollection.getDownloadDirTasks(
dirPath = "/documents",
localPath = "${getExternalFilesDir(null)}/documents",
status = DownloadDirState.RUNNING
)

Log.i(TAG, "运行中的任务: ${runningTasks.size}")

// 查询所有失败的文件任务
val failedFiles = smhCollection.getDownloadDirTasks(
dirPath = "/documents",
localPath = "${getExternalFilesDir(null)}/documents",
directoryFilter = DirectoryFilter.ONLY_FILE,
status = DownloadDirState.FAILURE
)

Log.i(TAG, "失败的文件: ${failedFiles.size}")

// 重试失败的任务
failedFiles.forEach { task ->
Log.i(TAG, "重试任务: ${task.path}")
task.start()
}

// 统计任务数量
val totalCount = smhCollection.countDownloadDirTasks(
dirPath = "/documents",
localPath = "${getExternalFilesDir(null)}/documents"
)

val completeCount = smhCollection.countDownloadDirTasks(
dirPath = "/documents",
localPath = "${getExternalFilesDir(null)}/documents",
status = DownloadDirState.COMPLETE
)

Log.i(TAG, "总任务数: $totalCount, 已完成: $completeCount")
}
}

示例3:应用重启后恢复任务

class MyApplication : Application() {
lateinit var smhCollection: SMHCollection

override fun onCreate() {
super.onCreate()

// 初始化SMHCollection
smhCollection = SMHCollection(
context = this,
user = MySMHUser(),
customHost = "<libraryId>.api.tencentsmh.cn"
)

// 恢复运行中的任务
lifecycleScope.launch {
smhCollection.restoreRunningTasks()
Log.i(TAG, "已恢复运行中的任务")
}
}
}