diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockInterceptor.kt b/Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockInterceptor.kt index 3590ad78..469e8294 100644 --- a/Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockInterceptor.kt +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockInterceptor.kt @@ -17,7 +17,7 @@ package com.arialyy.aria.http.download import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.inf.IBlockManager -import com.arialyy.aria.core.task.BlockUtil +import com.arialyy.aria.util.BlockUtil import com.arialyy.aria.core.task.ITask import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.TaskChain @@ -65,6 +65,7 @@ internal class HttpDBlockInterceptor : ITaskInterceptor { return chain.proceed(chain.getTask()) } val blockNum = checkRecord() + task.taskState.taskRecord = taskRecord chain.blockManager.setBlockNum(blockNum) val result = checkBlock() if (result != TaskResp.CODE_SUCCESS) { diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt b/Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt index 48a92123..d3c20d8c 100644 --- a/Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt @@ -15,19 +15,54 @@ */ package com.arialyy.aria.http.download +import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.listener.AbsEventListener +import com.arialyy.aria.core.listener.ISchedulers import com.arialyy.aria.core.task.DownloadTask +import com.arialyy.aria.core.task.TaskCachePool +import com.arialyy.aria.exception.AriaException +import com.arialyy.aria.orm.entity.DEntity +import com.arialyy.aria.util.BlockUtil +import com.arialyy.aria.util.FileUri import com.arialyy.aria.util.FileUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import timber.log.Timber +import java.io.File class HttpDEventListener(task: DownloadTask) : AbsEventListener(task) { - override fun handleCancel() { + /** + * merge block file, if merge fail call [onFail] + */ + override fun onComplete() { + val b = BlockUtil.mergeFile(task.taskState.taskRecord) + if (!b) { + Timber.e("merge block fail") + onFail(false, AriaException("merge block fail")) + return + } + handleSpeed(0) + sendInState2Target(ISchedulers.COMPLETE) } - override fun handleComplete() { + /** + * 1.remove all block + * 2.remove record + */ + override fun handleCancel() { + val file = FileUri.getPathByUri(task.taskState.taskRecord.filePath)?.let { File(it) } + file?.parentFile?.let { + FileUtils.deleteDir(it) + } + DuaContext.duaScope.launch(Dispatchers.IO) { + val entity = TaskCachePool.getEntity(taskId = task.taskId) + val db = DuaContext.getServiceManager().getDbService().getDuaDb() + db.getRecordDao().deleteTaskRecord(task.taskState.taskRecord) + db.getDEntityDao().delete(entity as DEntity) + } } - } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java index 69acfd10..fe503a02 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java @@ -43,11 +43,9 @@ public abstract class AbsEventListener implements IEventListener { */ protected abstract void handleCancel(); - protected abstract void handleComplete(); - protected AbsEventListener(ITask task) { this.outHandler = DuaContext.INSTANCE.getServiceManager().getSchedulerHandler(); - mTask = new WeakReference<>(task).get(); + mTask = task; mUpdateInterval = AriaConfig.getInstance().getCConfig().getUpdateInterval(); mLastLen = task.getTaskState().getCurProgress(); mLastSaveTime = System.currentTimeMillis(); @@ -96,12 +94,6 @@ public abstract class AbsEventListener implements IEventListener { sendInState2Target(ISchedulers.STOP); } - @Override public void onComplete() { - saveData(IEntity.STATE_COMPLETE, mTask.getTaskState().getFileSize()); - handleSpeed(0); - sendInState2Target(ISchedulers.COMPLETE); - } - @Override public void onCancel() { saveData(IEntity.STATE_CANCEL, -1); handleSpeed(0); @@ -127,7 +119,7 @@ public abstract class AbsEventListener implements IEventListener { } } - private void handleSpeed(long speed) { + protected void handleSpeed(long speed) { if (mUpdateInterval != 1000) { speed = speed * 1000 / mUpdateInterval; } @@ -153,9 +145,6 @@ public abstract class AbsEventListener implements IEventListener { return; } - if (state == IEntity.STATE_COMPLETE) { - handleComplete(); - } TaskCachePool.INSTANCE.updateState(mTask.getTaskId(), state, location); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskState.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskState.kt index a0311405..c53ccc9e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskState.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskState.kt @@ -18,6 +18,7 @@ package com.arialyy.aria.core.task import com.arialyy.aria.core.config.Configuration import com.arialyy.aria.core.inf.IEntity import com.arialyy.aria.orm.entity.BlockRecord +import com.arialyy.aria.orm.entity.TaskRecord import com.arialyy.aria.util.CommonUtil /** @@ -27,6 +28,8 @@ import com.arialyy.aria.util.CommonUtil **/ class TaskState { + lateinit var taskRecord: TaskRecord + /** * need to try again?,default: false */ @@ -44,7 +47,6 @@ class TaskState { */ var curProgress: Long = 0 - /** * Bytes transferred in 1 second, if file size 0, return 0 * curSpeed, unit: byte/s diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/entity/DEntity.kt b/PublicComponent/src/main/java/com/arialyy/aria/orm/entity/DEntity.kt index 4b3acafe..ce570c4d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/entity/DEntity.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/entity/DEntity.kt @@ -54,12 +54,10 @@ data class DEntity( ) : BaseEntity() { override fun update() { - fun update() { - updateTime = System.currentTimeMillis() - DuaContext.duaScope.launch(Dispatchers.IO) { - DuaContext.getServiceManager().getDbService().getDuaDb()?.getDEntityDao() - ?.update(this@DEntity) - } + updateTime = System.currentTimeMillis() + DuaContext.duaScope.launch(Dispatchers.IO) { + DuaContext.getServiceManager().getDbService().getDuaDb().getDEntityDao() + .update(this@DEntity) } } } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/BlockUtil.kt b/PublicComponent/src/main/java/com/arialyy/aria/util/BlockUtil.kt similarity index 64% rename from PublicComponent/src/main/java/com/arialyy/aria/core/task/BlockUtil.kt rename to PublicComponent/src/main/java/com/arialyy/aria/util/BlockUtil.kt index 010f6bff..5ddd6332 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/BlockUtil.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/BlockUtil.kt @@ -1,13 +1,12 @@ -package com.arialyy.aria.core.task +package com.arialyy.aria.util import android.net.Uri import com.arialyy.aria.orm.entity.BlockRecord import com.arialyy.aria.orm.entity.TaskRecord -import com.arialyy.aria.util.FileUri -import com.arialyy.aria.util.FileUtil -import com.arialyy.aria.util.FileUtils import timber.log.Timber import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream /* * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) @@ -78,8 +77,23 @@ object BlockUtil { * merge block file,if success,return true else return false */ fun mergeFile(record: TaskRecord): Boolean { - val targetF = File(record.filePath) - val dir = targetF.parentFile ?: return false + val blockRecordList = record.blockList + val targetPath = FileUri.getPathByUri(record.filePath) + if (blockRecordList.isEmpty()) { + Timber.e("block record list empty") + return false + } + if (targetPath.isNullOrEmpty()) { + Timber.e("invalid uri: $targetPath") + return false + } + val fileList = arrayListOf() + blockRecordList.forEach { + fileList.add(File(it.blockPath)) + } + + val targetF = File(targetPath) + val dir = targetF.parentFile val fileName = targetF.name if (record.blockNum == 1) { // if this task not support blocks or fileSize < 5m, just need rename @@ -98,6 +112,43 @@ object BlockUtil { } blockList.add(subF) } - return FileUtil.mergeFile(record.filePath, blockList) + return mergeFile(targetF, blockList) + } + + /** + * 合并文件 + * + * @param targetFile 目标文件 + * @param blockList 分块列表 + * @return `true` 合并成功,`false`合并失败 + */ + fun mergeFile(targetFile: File, blockList: List): Boolean { + Timber.d("开始合并文件") + if (targetFile.exists() && targetFile.isDirectory) { + Timber.w("路径【%s】是文件夹,将删除该文件夹", targetFile.path) + FileUtils.deleteDir(targetFile) + } + if (!targetFile.exists()) { + FileUtils.createFile(targetFile) + } + + val startTime = System.currentTimeMillis() + var fileLen: Long = 0 + FileOutputStream(targetFile).use { fos -> + fos.channel.use { foc -> + for (block in blockList) { + if (!block.exists()) { + Timber.d("合并文件失败,文件【${block.path}】不存在") + return false + } + FileInputStream(block).channel.use { fic -> + foc.transferFrom(fic, fileLen, block.length()) + fileLen += block.length() + } + } + } + } + Timber.d("merge file time:${System.currentTimeMillis() - startTime}ms, fileSize = $fileLen") + return false } } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java index b4c7ca0e..284f9f9d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java @@ -111,59 +111,9 @@ public class FileUtil { return String.format("%s/.%s_%s", file.getParent(), file.getName(), bandWidth); } - /** - * 创建目录 当目录不存在的时候创建文件,否则返回false - */ - public static boolean createDir(String path) { - File file = new File(path); - if (!file.exists()) { - if (!file.mkdirs()) { - ALog.d(TAG, "创建失败,请检查路径和是否配置文件权限!"); - return false; - } - return true; - } - return false; - } - /** - * 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件 - * - * @return {@code true} 创建成功、{@code false} 创建失败 - */ - public static boolean createFile(String path) { - if (TextUtils.isEmpty(path)) { - ALog.e(TAG, "文件路径不能为null"); - return false; - } - return createFile(new File(path)); - } - /** - * 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件 - * - * @return {@code true} 创建成功、{@code false} 创建失败 - */ - public static boolean createFile(File file) { - if (file.getParentFile() == null || !file.getParentFile().exists()) { - ALog.d(TAG, "目标文件所在路径不存在,准备创建……"); - if (!createDir(file.getParent())) { - ALog.d(TAG, "创建目录文件所在的目录失败!文件路径【" + file.getPath() + "】"); - } - } - // 文件存在,删除文件 - deleteFile(file); - try { - if (file.createNewFile()) { - //ALog.d(TAG, "创建文件成功:" + file.getAbsolutePath()); - return true; - } - } catch (IOException e) { - e.printStackTrace(); - return false; - } - return false; - } + /** * 创建文件名,如果url链接有后缀名,则使用url中的后缀名 @@ -191,59 +141,6 @@ public class FileUtil { return fileName; } - /** - * 删除文件 - * - * @param path 文件路径 - * @return {@code true}删除成功、{@code false}删除失败 - */ - public static boolean deleteFile(String path) { - if (TextUtils.isEmpty(path)) { - ALog.e(TAG, "删除文件失败,路径为空"); - return false; - } - return deleteFile(new File(path)); - } - - /** - * 删除文件 - * - * @param file 文件路径 - * @return {@code true}删除成功、{@code false}删除失败 - */ - public static boolean deleteFile(File file) { - if (file.exists()) { - final File to = new File(file.getAbsolutePath() + System.currentTimeMillis()); - if (file.renameTo(to)) { - return to.delete(); - } else { - return file.delete(); - } - } - return false; - } - - /** - * 删除文件夹 - */ - public static boolean deleteDir(File dirFile) { - // 如果dir对应的文件不存在,则退出 - if (!dirFile.exists()) { - return false; - } - - if (dirFile.isFile()) { - return dirFile.delete(); - } else { - - for (File file : dirFile.listFiles()) { - deleteDir(file); - } - } - - return dirFile.delete(); - } - /** * 将对象写入文件 * @@ -317,66 +214,7 @@ public class FileUtil { return null; } - /** - * 合并文件 - * - * @param targetPath 目标文件 - * @param blockList 分块列表 - * @return {@code true} 合并成功,{@code false}合并失败 - */ - public static boolean mergeFile(String targetPath, List blockList) { - Timber.d("开始合并文件"); - File file = new File(targetPath); - FileOutputStream fos = null; - FileChannel foc = null; - long startTime = System.currentTimeMillis(); - try { - if (file.exists() && file.isDirectory()) { - ALog.w(TAG, String.format("路径【%s】是文件夹,将删除该文件夹", targetPath)); - FileUtil.deleteDir(file); - } - if (!file.exists()) { - FileUtil.createFile(file); - } - fos = new FileOutputStream(targetPath); - foc = fos.getChannel(); - List streams = new LinkedList<>(); - long fileLen = 0; - for (File block : blockList) { - if (!block.exists()) { - ALog.d(TAG, String.format("合并文件失败,文件【%s】不存在", block.getPath())); - for (FileInputStream fis : streams) { - fis.close(); - } - streams.clear(); - - return false; - } - FileInputStream fis = new FileInputStream(block); - FileChannel fic = fis.getChannel(); - foc.transferFrom(fic, fileLen, block.length()); - fileLen += block.length(); - fis.close(); - } - ALog.d(TAG, String.format("合并文件耗时:%sms", (System.currentTimeMillis() - startTime))); - return true; - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (foc != null) { - foc.close(); - } - if (fos != null) { - fos.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - return false; - } /** * 合并sftp的分块文件,sftp的分块可能会超出规定的长度,因此需要使用本方法 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt index d8e8b27f..33bb924e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt @@ -20,9 +20,9 @@ import android.database.Cursor import android.net.Uri import android.provider.OpenableColumns import com.arialyy.aria.core.DuaContext -import com.arialyy.aria.orm.entity.BlockRecord import timber.log.Timber import java.io.File +import java.io.IOException import java.io.InputStream import java.util.Locale @@ -34,20 +34,99 @@ object FileUtils { RegexOption.IGNORE_CASE ) - fun mergeBlock(blockRecordList: List, targetPath: Uri): Boolean { - if (blockRecordList.isEmpty()) { - Timber.e("block record list empty") + /** + * 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件 + * + * @return `true` 创建成功、`false` 创建失败 + */ + fun createFile(path: String): Boolean { + return createFile(File(path)) + } + + /** + * 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件 + * + * @return `true` 创建成功、`false` 创建失败 + */ + fun createFile(file: File): Boolean { + if (file.parentFile == null || file.parentFile?.exists() == false) { + Timber.d("目标文件所在路径不存在,准备创建……") + if (!createDir(file.parent!!)) { + Timber.d("创建目录文件所在的目录失败!文件路径【" + file.path + "】") + } + } + // 文件存在,删除文件 + deleteFile(file) + try { + if (file.createNewFile()) { + //ALog.d(TAG, "创建文件成功:" + file.getAbsolutePath()); + return true + } + } catch (e: IOException) { + e.printStackTrace() return false } - if (!uriEffective(targetPath)) { - Timber.e("invalid uri: $targetPath") + return false + } + + /** + * 创建目录 当目录不存在的时候创建文件,否则返回false + */ + fun createDir(path: String): Boolean { + val file = File(path) + if (!file.exists()) { + if (!file.mkdirs()) { + Timber.d("创建失败,请检查路径和是否配置文件权限!") + return false + } + return true + } + return false + } + + /** + * 删除文件夹 + */ + fun deleteDir(dirFile: File): Boolean { + // 如果dir对应的文件不存在,则退出 + if (!dirFile.exists()) { return false } - val fileList = arrayListOf() - blockRecordList.forEach { - fileList.add(File(it.blockPath)) + if (dirFile.isFile) { + return dirFile.delete() + } + dirFile.listFiles()?.forEach { + deleteDir(it) + } + return dirFile.delete() + } + + /** + * 删除文件 + * + * @param path 文件路径 + * @return `true`删除成功、`false`删除失败 + */ + fun deleteFile(path: String): Boolean { + return deleteFile(File(path)) + } + + /** + * 删除文件 + * + * @param file 文件路径 + * @return `true`删除成功、`false`删除失败 + */ + fun deleteFile(file: File): Boolean { + if (file.exists()) { + val to = File(file.absolutePath + System.currentTimeMillis()) + return if (file.renameTo(to)) { + to.delete() + } else { + file.delete() + } } - return FileUtil.mergeFile() + return false } /**