update HttpDEventListener.kt

v4
laoyuyu 2 years ago
parent f0c63d72b3
commit e2d706fbc6
  1. 3
      Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockInterceptor.kt
  2. 41
      Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt
  3. 15
      PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java
  4. 4
      PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskState.kt
  5. 10
      PublicComponent/src/main/java/com/arialyy/aria/orm/entity/DEntity.kt
  6. 65
      PublicComponent/src/main/java/com/arialyy/aria/util/BlockUtil.kt
  7. 164
      PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java
  8. 99
      PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt

@ -17,7 +17,7 @@ package com.arialyy.aria.http.download
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IBlockManager 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.ITask
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskChain import com.arialyy.aria.core.task.TaskChain
@ -65,6 +65,7 @@ internal class HttpDBlockInterceptor : ITaskInterceptor {
return chain.proceed(chain.getTask()) return chain.proceed(chain.getTask())
} }
val blockNum = checkRecord() val blockNum = checkRecord()
task.taskState.taskRecord = taskRecord
chain.blockManager.setBlockNum(blockNum) chain.blockManager.setBlockNum(blockNum)
val result = checkBlock() val result = checkBlock()
if (result != TaskResp.CODE_SUCCESS) { if (result != TaskResp.CODE_SUCCESS) {

@ -15,19 +15,54 @@
*/ */
package com.arialyy.aria.http.download 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.AbsEventListener
import com.arialyy.aria.core.listener.ISchedulers
import com.arialyy.aria.core.task.DownloadTask 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 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) { 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)
}
} }
} }

@ -43,11 +43,9 @@ public abstract class AbsEventListener implements IEventListener {
*/ */
protected abstract void handleCancel(); protected abstract void handleCancel();
protected abstract void handleComplete();
protected AbsEventListener(ITask task) { protected AbsEventListener(ITask task) {
this.outHandler = DuaContext.INSTANCE.getServiceManager().getSchedulerHandler(); this.outHandler = DuaContext.INSTANCE.getServiceManager().getSchedulerHandler();
mTask = new WeakReference<>(task).get(); mTask = task;
mUpdateInterval = AriaConfig.getInstance().getCConfig().getUpdateInterval(); mUpdateInterval = AriaConfig.getInstance().getCConfig().getUpdateInterval();
mLastLen = task.getTaskState().getCurProgress(); mLastLen = task.getTaskState().getCurProgress();
mLastSaveTime = System.currentTimeMillis(); mLastSaveTime = System.currentTimeMillis();
@ -96,12 +94,6 @@ public abstract class AbsEventListener implements IEventListener {
sendInState2Target(ISchedulers.STOP); sendInState2Target(ISchedulers.STOP);
} }
@Override public void onComplete() {
saveData(IEntity.STATE_COMPLETE, mTask.getTaskState().getFileSize());
handleSpeed(0);
sendInState2Target(ISchedulers.COMPLETE);
}
@Override public void onCancel() { @Override public void onCancel() {
saveData(IEntity.STATE_CANCEL, -1); saveData(IEntity.STATE_CANCEL, -1);
handleSpeed(0); 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) { if (mUpdateInterval != 1000) {
speed = speed * 1000 / mUpdateInterval; speed = speed * 1000 / mUpdateInterval;
} }
@ -153,9 +145,6 @@ public abstract class AbsEventListener implements IEventListener {
return; return;
} }
if (state == IEntity.STATE_COMPLETE) {
handleComplete();
}
TaskCachePool.INSTANCE.updateState(mTask.getTaskId(), state, location); TaskCachePool.INSTANCE.updateState(mTask.getTaskId(), state, location);
} }
} }

@ -18,6 +18,7 @@ package com.arialyy.aria.core.task
import com.arialyy.aria.core.config.Configuration import com.arialyy.aria.core.config.Configuration
import com.arialyy.aria.core.inf.IEntity import com.arialyy.aria.core.inf.IEntity
import com.arialyy.aria.orm.entity.BlockRecord import com.arialyy.aria.orm.entity.BlockRecord
import com.arialyy.aria.orm.entity.TaskRecord
import com.arialyy.aria.util.CommonUtil import com.arialyy.aria.util.CommonUtil
/** /**
@ -27,6 +28,8 @@ import com.arialyy.aria.util.CommonUtil
**/ **/
class TaskState { class TaskState {
lateinit var taskRecord: TaskRecord
/** /**
* need to try again?default: false * need to try again?default: false
*/ */
@ -44,7 +47,6 @@ class TaskState {
*/ */
var curProgress: Long = 0 var curProgress: Long = 0
/** /**
* Bytes transferred in 1 second, if file size 0, return 0 * Bytes transferred in 1 second, if file size 0, return 0
* curSpeed, unit: byte/s * curSpeed, unit: byte/s

@ -54,12 +54,10 @@ data class DEntity(
) : BaseEntity() { ) : BaseEntity() {
override fun update() { override fun update() {
fun update() { updateTime = System.currentTimeMillis()
updateTime = System.currentTimeMillis() DuaContext.duaScope.launch(Dispatchers.IO) {
DuaContext.duaScope.launch(Dispatchers.IO) { DuaContext.getServiceManager().getDbService().getDuaDb().getDEntityDao()
DuaContext.getServiceManager().getDbService().getDuaDb()?.getDEntityDao() .update(this@DEntity)
?.update(this@DEntity)
}
} }
} }
} }

@ -1,13 +1,12 @@
package com.arialyy.aria.core.task package com.arialyy.aria.util
import android.net.Uri import android.net.Uri
import com.arialyy.aria.orm.entity.BlockRecord import com.arialyy.aria.orm.entity.BlockRecord
import com.arialyy.aria.orm.entity.TaskRecord 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 timber.log.Timber
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
/* /*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) * 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 * merge block file,if success,return true else return false
*/ */
fun mergeFile(record: TaskRecord): Boolean { fun mergeFile(record: TaskRecord): Boolean {
val targetF = File(record.filePath) val blockRecordList = record.blockList
val dir = targetF.parentFile ?: return false 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<File>()
blockRecordList.forEach {
fileList.add(File(it.blockPath))
}
val targetF = File(targetPath)
val dir = targetF.parentFile
val fileName = targetF.name val fileName = targetF.name
if (record.blockNum == 1) { if (record.blockNum == 1) {
// if this task not support blocks or fileSize < 5m, just need rename // if this task not support blocks or fileSize < 5m, just need rename
@ -98,6 +112,43 @@ object BlockUtil {
} }
blockList.add(subF) 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<File>): 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
} }
} }

@ -111,59 +111,9 @@ public class FileUtil {
return String.format("%s/.%s_%s", file.getParent(), file.getName(), bandWidth); 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中的后缀名 * 创建文件名如果url链接有后缀名则使用url中的后缀名
@ -191,59 +141,6 @@ public class FileUtil {
return fileName; 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; return null;
} }
/**
* 合并文件
*
* @param targetPath 目标文件
* @param blockList 分块列表
* @return {@code true} 合并成功{@code false}合并失败
*/
public static boolean mergeFile(String targetPath, List<File> 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<FileInputStream> 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的分块可能会超出规定的长度因此需要使用本方法 * 合并sftp的分块文件sftp的分块可能会超出规定的长度因此需要使用本方法

@ -20,9 +20,9 @@ import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.OpenableColumns import android.provider.OpenableColumns
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.orm.entity.BlockRecord
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.Locale import java.util.Locale
@ -34,20 +34,99 @@ object FileUtils {
RegexOption.IGNORE_CASE RegexOption.IGNORE_CASE
) )
fun mergeBlock(blockRecordList: List<BlockRecord>, 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 return false
} }
if (!uriEffective(targetPath)) { return false
Timber.e("invalid uri: $targetPath") }
/**
* 创建目录 当目录不存在的时候创建文件否则返回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 return false
} }
val fileList = arrayListOf<File>() if (dirFile.isFile) {
blockRecordList.forEach { return dirFile.delete()
fileList.add(File(it.blockPath)) }
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
} }
/** /**

Loading…
Cancel
Save