简介
本文档提供关于目录下载的接口说明以及 SDK 示例代码,支持递归下载整个目录树,包括所有子目录和文件。该功能具有以下特点:
递归下载:自动遍历并下载目录中的所有子目录和文件。
任务管理:支持启动、暂停、恢复、取消等完整的任务生命周期管理。
进度跟踪:实时监控下载进度和状态变化。
冲突处理:支持多种文件冲突策略(询问、重命名、覆盖)。
数据持久化:任务信息自动保存到数据库,支持应用重启后恢复。
查询过滤:支持按状态、类型、排序等多种条件查询任务。
注意:
协程环境:所有 API 都是挂起函数,需要在协程环境中调用。
回调线程:回调可能在后台线程执行,更新 UI 时需要切换到主线程。
任务持久化:任务信息自动保存到数据库,应用重启后可通过
restoreRunningTasks() 恢复。错误处理:下载失败时,可通过
errorType、errorCode、errorInfo 获取详细错误信息。冲突策略:建议使用
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 0Log.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 // 资源库 IDval spaceId: String? // 空间 IDval userId: String? // 用户 IDval 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 // 资源库 IDval spaceId: String? // 空间 IDval userId: String? // 用户 IDval 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: SMHCollectionprivate var downloadTask: SMHDownloadDirTask? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 初始化SMHCollectionsmhCollection = 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 0lifecycleScope.launch(Dispatchers.Main) {progressBar.progress = progresstvProgress.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: SMHCollectionoverride 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: SMHCollectionoverride fun onCreate() {super.onCreate()// 初始化SMHCollectionsmhCollection = SMHCollection(context = this,user = MySMHUser(),customHost = "<libraryId>.api.tencentsmh.cn")// 恢复运行中的任务lifecycleScope.launch {smhCollection.restoreRunningTasks()Log.i(TAG, "已恢复运行中的任务")}}}