From d239ecc1e7d2018e04a30b32edf98ba9a0389bce Mon Sep 17 00:00:00 2001 From: AriaLyy <511455842@qq.com> Date: Thu, 27 Jul 2017 16:54:00 +0800 Subject: [PATCH] =?UTF-8?q?FTP=20=E6=96=87=E4=BB=B6=E5=A4=B9=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/download/BaseGroupTarget.java | 6 +- .../core/download/DownloadGroupEntity.java | 4 +- .../aria/core/download/DownloadGroupTask.java | 11 +- .../aria/core/download/DownloadReceiver.java | 9 + .../core/download/FtpDirDownloadTarget.java | 15 +- .../aria/core/download/FtpDownloadTarget.java | 3 +- .../download/downloader/AbsFtpInfoThread.java | 44 +- .../download/downloader/AbsGroupUtil.java | 397 ++++++++++++++++++ .../download/downloader/AbsThreadTask.java | 2 + .../download/downloader/ConnectionHelp.java | 6 + .../downloader/DownloadGroupUtil.java | 339 +-------------- .../core/download/downloader/Downloader.java | 8 +- .../downloader/FtpDirDownloadUtil.java | 68 +++ .../download/downloader/FtpDirInfoThread.java | 33 ++ .../download/downloader/FtpThreadTask.java | 26 +- .../download/downloader/SubThreadConfig.java | 3 - .../arialyy/aria/core/inf/AbsTaskEntity.java | 18 +- .../java/com/arialyy/aria/core/inf/ITask.java | 28 +- .../simple/download/FtpDownloadActivity.java | 4 +- .../group/FTPDirDownloadActivity.java | 27 +- 20 files changed, 649 insertions(+), 402 deletions(-) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/BaseGroupTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/BaseGroupTarget.java index a1d44a9d..c6e1d129 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/BaseGroupTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/BaseGroupTarget.java @@ -32,15 +32,15 @@ abstract class BaseGroupTarget { List mUrls = new ArrayList<>(); + String mGroupName; /** * 子任务文件名 */ - List mSubTaskFileName = new ArrayList<>(); - String mGroupName; + private List mSubTaskFileName = new ArrayList<>(); /** * 是否已经设置了文件路径 */ - boolean isSetDirPathed = false; + private boolean isSetDirPathed = false; /** * 查询任务组实体,如果数据库不存在该实体,则新创建一个新的任务组实体 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java index da96b5ff..4d5354c2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java @@ -47,7 +47,7 @@ public class DownloadGroupEntity extends AbsGroupEntity { return subtask; } - void setSubTasks(List subTasks) { + public void setSubTasks(List subTasks) { this.subtask = subTasks; } @@ -63,7 +63,7 @@ public class DownloadGroupEntity extends AbsGroupEntity { return urls; } - void setUrls(List urls) { + public void setUrls(List urls) { this.urls = urls; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java index 6c668ad2..a6bd0452 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java @@ -18,8 +18,10 @@ package com.arialyy.aria.core.download; import android.os.Handler; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.downloader.DownloadGroupUtil; +import com.arialyy.aria.core.download.downloader.FtpDirDownloadUtil; import com.arialyy.aria.core.download.downloader.IDownloadUtil; import com.arialyy.aria.core.inf.AbsGroupTask; +import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.scheduler.ISchedulers; import com.arialyy.aria.util.CheckUtil; @@ -38,7 +40,14 @@ public class DownloadGroupTask extends AbsGroupTask { private final String TAG = "FtpDirDownloadTarget"; - private String serverIp, remotePath, mGroupName; + private String serverIp, remotePath; private int port; FtpDirDownloadTarget(String url, String targetName) { init(url); String[] pp = url.split("/")[2].split(":"); - this.serverIp = pp[0]; - this.port = Integer.parseInt(pp[1]); - mTaskEntity.requestType = AbsTaskEntity.FTP; + mTargetName = targetName; + serverIp = pp[0]; + port = Integer.parseInt(pp[1]); + mTaskEntity.requestType = AbsTaskEntity.FTP_DIR; + mTaskEntity.serverIp = serverIp; + mTaskEntity.port = port; remotePath = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()); if (TextUtils.isEmpty(remotePath)) { throw new NullPointerException("ftp服务器地址不能为null"); } - mTargetName = targetName; - int lastIndex = url.lastIndexOf("/"); - mTaskEntity.remotePath = remotePath; - mEntity.setDirPath(url.substring(lastIndex + 1, url.length())); } private void init(String key) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java index a750ee3c..b7dea7cc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java @@ -42,7 +42,8 @@ public class FtpDownloadTarget extends DownloadTarget { throw new NullPointerException("ftp服务器地址不能为null"); } int lastIndex = url.lastIndexOf("/"); - mTaskEntity.remotePath = remotePath; + mTaskEntity.serverIp = serverIp; + mTaskEntity.port = port; mEntity.setFileName(url.substring(lastIndex + 1, url.length())); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsFtpInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsFtpInfoThread.java index 98d34c73..7bcc4f05 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsFtpInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsFtpInfoThread.java @@ -19,7 +19,6 @@ import android.text.TextUtils; import android.util.Log; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsGroupEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.util.CommonUtil; import java.io.IOException; @@ -36,11 +35,10 @@ abstract class AbsFtpInfoThread mExeMap = new HashMap<>(); + + /** + * 下载失败的映射表,key为下载地址 + */ + Map mFailMap = new HashMap<>(); + + /** + * 下载器映射表,key为下载地址 + */ + private Map mDownloaderMap = new HashMap<>(); + + /** + * 该任务组对应的所有任务 + */ + private Map mTasksMap = new HashMap<>(); + //已经完成的任务数 + private int mCompleteNum = 0; + //失败的任务数 + private int mFailNum = 0; + //实际的下载任务数 + int mActualTaskNum = 0; + + AbsGroupUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity taskEntity) { + mListener = listener; + mTaskEntity = taskEntity; + mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + List tasks = + DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); + if (tasks != null && !tasks.isEmpty()) { + for (DownloadTaskEntity te : tasks) { + mTasksMap.put(te.getEntity().getDownloadUrl(), te); + } + } + for (DownloadEntity entity : mTaskEntity.entity.getSubTask()) { + File file = new File(entity.getDownloadPath()); + if (entity.getState() == IEntity.STATE_COMPLETE && file.exists()) { + mCompleteNum++; + mInitNum++; + mCurrentLocation += entity.getFileSize(); + } else { + mExeMap.put(entity.getDownloadUrl(), createChildDownloadTask(entity)); + mCurrentLocation += entity.getCurrentProgress(); + mActualTaskNum++; + } + mTotalSize += entity.getFileSize(); + } + } + + @Override public long getFileSize() { + return mTotalSize; + } + + @Override public long getCurrentLocation() { + return mCurrentLocation; + } + + @Override public boolean isDownloading() { + return isRunning; + } + + @Override public void cancelDownload() { + closeTimer(false); + mListener.onCancel(); + onCancel(); + if (!mExePool.isShutdown()) { + mExePool.shutdown(); + } + + Set keys = mDownloaderMap.keySet(); + for (String key : keys) { + Downloader dt = mDownloaderMap.get(key); + if (dt != null) { + dt.cancelDownload(); + } + } + delDownloadInfo(); + mTaskEntity.deleteData(); + } + + public void onCancel() { + + } + + /** + * 删除所有子任务的下载信息 + */ + private void delDownloadInfo() { + List tasks = + DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); + if (tasks == null || tasks.isEmpty()) return; + for (DownloadTaskEntity taskEntity : tasks) { + CommonUtil.delDownloadTaskConfig(taskEntity.removeFile, taskEntity); + } + } + + @Override public void stopDownload() { + closeTimer(false); + mListener.onStop(mCurrentLocation); + onStop(); + if (!mExePool.isShutdown()) { + mExePool.shutdown(); + } + + Set keys = mDownloaderMap.keySet(); + for (String key : keys) { + Downloader dt = mDownloaderMap.get(key); + if (dt != null) { + dt.stopDownload(); + } + } + } + + protected void onStop() { + + } + + @Override public void startDownload() { + isRunning = true; + mFailNum = 0; + mListener.onPre(); + onStart(); + } + + protected void onStart() { + + } + + @Override public void resumeDownload() { + startDownload(); + mListener.onResume(mCurrentLocation); + } + + @Override public void setMaxSpeed(double maxSpeed) { + + } + + private void closeTimer(boolean isRunning) { + this.isRunning = isRunning; + if (mTimer != null) { + mTimer.purge(); + mTimer.cancel(); + } + } + + /** + * 开始进度流程 + */ + void startRunningFlow() { + closeTimer(true); + mListener.onPostPre(mTotalSize); + mListener.onStart(mCurrentLocation); + mTimer = new Timer(true); + mTimer.schedule(new TimerTask() { + @Override public void run() { + if (!isRunning) { + closeTimer(false); + } else if (mCurrentLocation >= 0) { + mListener.onProgress(mCurrentLocation); + } + } + }, 0, 1000); + } + + /** + * 启动子任务下载器 + */ + void startChildDownload(DownloadTaskEntity taskEntity) { + ChildDownloadListener listener = new ChildDownloadListener(taskEntity); + Downloader dt = new Downloader(listener, taskEntity); + mDownloaderMap.put(taskEntity.getEntity().getDownloadUrl(), dt); + if (mExePool.isShutdown()) return; + mExePool.execute(dt); + } + + /** + * 创建子任务下载信息 + */ + DownloadTaskEntity createChildDownloadTask(DownloadEntity entity) { + DownloadTaskEntity taskEntity = mTasksMap.get(entity.getDownloadUrl()); + if (taskEntity != null) { + taskEntity.entity = entity; + //ftp登录的 + taskEntity.userName = mTaskEntity.userName; + taskEntity.userPw = mTaskEntity.userPw; + taskEntity.account = mTaskEntity.account; + return taskEntity; + } + taskEntity = new DownloadTaskEntity(); + taskEntity.entity = entity; + taskEntity.headers = mTaskEntity.headers; + taskEntity.requestEnum = mTaskEntity.requestEnum; + taskEntity.redirectUrlKey = mTaskEntity.redirectUrlKey; + taskEntity.removeFile = mTaskEntity.removeFile; + taskEntity.groupName = mTaskEntity.key; + taskEntity.isGroupTask = true; + taskEntity.requestType = mTaskEntity.requestType; + //ftp登录的 + taskEntity.userName = mTaskEntity.userName; + taskEntity.userPw = mTaskEntity.userPw; + taskEntity.account = mTaskEntity.account; + taskEntity.key = entity.getDownloadPath(); + taskEntity.save(); + return taskEntity; + } + + /** + * 子任务事件监听 + */ + private class ChildDownloadListener implements IDownloadListener { + + DownloadTaskEntity taskEntity; + DownloadEntity entity; + + long lastLen = 0; + + ChildDownloadListener(DownloadTaskEntity entity) { + this.taskEntity = entity; + this.entity = taskEntity.getEntity(); + lastLen = this.entity.getCurrentProgress(); + this.entity.setFailNum(0); + } + + @Override public void onPre() { + saveData(IEntity.STATE_PRE, -1); + } + + @Override public void onPostPre(long fileSize) { + entity.setFileSize(fileSize); + entity.setConvertFileSize(CommonUtil.formatFileSize(fileSize)); + saveData(IEntity.STATE_POST_PRE, -1); + } + + @Override public void onResume(long resumeLocation) { + saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); + lastLen = resumeLocation; + } + + @Override public void onStart(long startLocation) { + saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); + lastLen = startLocation; + } + + @Override public void onProgress(long currentLocation) { + long speed = currentLocation - lastLen; + mCurrentLocation += speed; + lastLen = currentLocation; + entity.setCurrentProgress(currentLocation); + handleSpeed(speed); + } + + @Override public void onStop(long stopLocation) { + saveData(IEntity.STATE_STOP, stopLocation); + handleSpeed(0); + mListener.onSubStop(entity); + } + + @Override public void onCancel() { + saveData(IEntity.STATE_CANCEL, -1); + handleSpeed(0); + mListener.onSubCancel(entity); + } + + @Override public void onComplete() { + saveData(IEntity.STATE_COMPLETE, entity.getFileSize()); + mCompleteNum++; + handleSpeed(0); + mListener.onSubComplete(entity); + //如果子任务完成的数量和总任务数一致,表示任务组任务已经完成 + if (mCompleteNum >= mTaskEntity.getEntity().getSubTask().size()) { + closeTimer(false); + mListener.onComplete(); + } else if (mCompleteNum + mFailNum >= mActualTaskNum) { + //如果子任务完成数量加上失败的数量和总任务数一致,则任务组停止下载 + closeTimer(false); + } + } + + @Override public void onFail() { + entity.setFailNum(entity.getFailNum() + 1); + saveData(IEntity.STATE_FAIL, lastLen); + handleSpeed(0); + reTry(); + } + + /** + * 失败后重试下载,如果失败次数超过5次,不再重试 + */ + private void reTry() { + synchronized (AriaManager.LOCK) { + if (entity.getFailNum() < 5 && isRunning) { + reStartTask(); + } else { + mFailNum++; + mListener.onSubFail(entity); + //如果失败的任务数大于实际的下载任务数,任务组停止下载 + if (mFailNum >= mActualTaskNum) { + closeTimer(false); + mListener.onStop(mCurrentLocation); + } + } + } + } + + private void reStartTask() { + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override public void run() { + Downloader dt = mDownloaderMap.get(entity.getDownloadUrl()); + dt.startDownload(); + } + }, 3000); + } + + private void handleSpeed(long speed) { + entity.setSpeed(speed); + entity.setConvertSpeed(speed <= 0 ? "" : CommonUtil.formatFileSize(speed) + "/s"); + } + + private void saveData(int state, long location) { + entity.setState(state); + entity.setComplete(state == IEntity.STATE_COMPLETE); + if (entity.isComplete()) { + entity.setCompleteTime(System.currentTimeMillis()); + entity.setCurrentProgress(entity.getFileSize()); + } else if (location > 0) { + entity.setCurrentProgress(location); + } + entity.update(); + } + + @Override public void supportBreakpoint(boolean support) { + + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsThreadTask.java index c3710eaa..e748b58c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsThreadTask.java @@ -154,6 +154,8 @@ abstract class AbsThreadTask implements Runnable { STATE.isStop = true; if (ex != null) { Log.e(TAG, msg + "\n" + CommonUtil.getPrintException(ex)); + }else { + Log.e(TAG, msg); } if (mConfig.IS_SUPPORT_BREAK_POINT) { writeConfig(false, currentLocation); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java index 028db87c..72f1c056 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java @@ -26,12 +26,18 @@ import java.util.Set; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; +import org.apache.commons.net.ftp.FTPClient; /** * Created by lyy on 2017/1/18. * 链接帮助类 */ class ConnectionHelp { + /** + * FTP 服务器编码 + */ + static String SERVER_CHARSET = "ISO-8859-1"; + /** * 处理链接 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java index 55320559..a0e0fd0a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java @@ -16,19 +16,10 @@ package com.arialyy.aria.core.download.downloader; import android.util.SparseArray; -import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.CommonUtil; -import java.io.File; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -36,153 +27,37 @@ import java.util.concurrent.Executors; * Created by AriaL on 2017/6/30. * 任务组下载工具 */ -public class DownloadGroupUtil implements IDownloadUtil { +public class DownloadGroupUtil extends AbsGroupUtil implements IDownloadUtil { private final String TAG = "DownloadGroupUtil"; - /** - * 任务组所有任务总大小 - */ - private long mTotalSize = 0; - private long mCurrentLocation = 0; private ExecutorService mInfoPool; - private ExecutorService mExePool; - private IDownloadGroupListener mListener; - private DownloadGroupTaskEntity mTaskEntity; - private boolean isRunning = true; - private Timer mTimer; - /** - * 初始化完成的任务书数 - */ - private int mInitNum = 0; - /** - * 初始化失败的任务数 - */ - private int mInitFailNum = 0; - /** - * 保存所有没有下载完成的任务,key为下载地址 - */ - private Map mExeMap = new HashMap<>(); - - /** - * 下载失败的映射表,key为下载地址 - */ - private Map mFailMap = new HashMap<>(); - - /** - * 下载器映射表,key为下载地址 - */ - private Map mDownloaderMap = new HashMap<>(); /** * 文件信息回调组 */ private SparseArray mFileInfoCallbacks = new SparseArray<>(); - /** - * 该任务组对应的所有任务 - */ - private Map mTasksMap = new HashMap<>(); - //已经完成的任务数 - private int mCompleteNum = 0; - //失败的任务数 - private int mFailNum = 0; - //实际的下载任务数 - private int mActualTaskNum = 0; public DownloadGroupUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity taskEntity) { - mListener = listener; - mTaskEntity = taskEntity; + super(listener, taskEntity); mInfoPool = Executors.newCachedThreadPool(); - mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - mActualTaskNum = mTaskEntity.entity.getSubTask().size(); - List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); - if (tasks != null && !tasks.isEmpty()) { - for (DownloadTaskEntity te : tasks) { - mTasksMap.put(te.getEntity().getDownloadUrl(), te); - } - } - for (DownloadEntity entity : mTaskEntity.entity.getSubTask()) { - File file = new File(entity.getDownloadPath()); - if (entity.getState() == IEntity.STATE_COMPLETE && file.exists()) { - mCompleteNum++; - mInitNum++; - mCurrentLocation += entity.getFileSize(); - } else { - mExeMap.put(entity.getDownloadUrl(), createChildDownloadTask(entity)); - mCurrentLocation += entity.getCurrentProgress(); - } - mTotalSize += entity.getFileSize(); - } } - @Override public long getFileSize() { - return mTotalSize; - } - - @Override public long getCurrentLocation() { - return mCurrentLocation; - } - - @Override public boolean isDownloading() { - return isRunning; - } - - @Override public void cancelDownload() { - closeTimer(false); - mListener.onCancel(); + @Override public void onCancel() { + super.onCancel(); if (!mInfoPool.isShutdown()) { mInfoPool.shutdown(); } - if (!mExePool.isShutdown()) { - mExePool.shutdown(); - } - - Set keys = mDownloaderMap.keySet(); - for (String key : keys) { - Downloader dt = mDownloaderMap.get(key); - if (dt != null) { - dt.cancelDownload(); - } - } - delDownloadInfo(); - mTaskEntity.deleteData(); } - /** - * 删除所有子任务的下载信息 - */ - private void delDownloadInfo() { - List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mTaskEntity.key); - if (tasks == null || tasks.isEmpty()) return; - for (DownloadTaskEntity taskEntity : tasks) { - CommonUtil.delDownloadTaskConfig(taskEntity.removeFile, taskEntity); - } - } - - @Override public void stopDownload() { - closeTimer(false); - mListener.onStop(mCurrentLocation); + @Override protected void onStop() { + super.onStop(); if (!mInfoPool.isShutdown()) { mInfoPool.shutdown(); } - if (!mExePool.isShutdown()) { - mExePool.shutdown(); - } - - Set keys = mDownloaderMap.keySet(); - for (String key : keys) { - Downloader dt = mDownloaderMap.get(key); - if (dt != null) { - dt.stopDownload(); - } - } } - @Override public void startDownload() { - isRunning = true; - mFailNum = 0; + @Override protected void onStart() { + super.onStart(); Set keys = mExeMap.keySet(); - mListener.onPre(); int i = 0; for (String key : keys) { DownloadTaskEntity taskEntity = mExeMap.get(key); @@ -199,15 +74,6 @@ public class DownloadGroupUtil implements IDownloadUtil { if (i == mExeMap.size()) startRunningFlow(); } - @Override public void resumeDownload() { - startDownload(); - mListener.onResume(mCurrentLocation); - } - - @Override public void setMaxSpeed(double maxSpeed) { - - } - /** * 创建文件信息获取线程 */ @@ -254,193 +120,4 @@ public class DownloadGroupUtil implements IDownloadUtil { } return new HttpFileInfoThread(taskEntity, callback); } - - private void closeTimer(boolean isRunning) { - this.isRunning = isRunning; - if (mTimer != null) { - mTimer.purge(); - mTimer.cancel(); - } - } - - /** - * 开始进度流程 - */ - private void startRunningFlow() { - closeTimer(true); - mListener.onPostPre(mTotalSize); - mListener.onStart(mCurrentLocation); - mTimer = new Timer(true); - mTimer.schedule(new TimerTask() { - @Override public void run() { - if (!isRunning) { - closeTimer(false); - } else if (mCurrentLocation >= 0) { - mListener.onProgress(mCurrentLocation); - } - } - }, 0, 1000); - } - - /** - * 启动子任务下载器 - */ - private void startChildDownload(DownloadTaskEntity taskEntity) { - ChildDownloadListener listener = new ChildDownloadListener(taskEntity); - Downloader dt = new Downloader(listener, taskEntity); - mDownloaderMap.put(taskEntity.getEntity().getDownloadUrl(), dt); - if (mExePool.isShutdown()) return; - mExePool.execute(dt); - } - - /** - * 创建子任务下载信息 - */ - private DownloadTaskEntity createChildDownloadTask(DownloadEntity entity) { - DownloadTaskEntity taskEntity = mTasksMap.get(entity.getDownloadUrl()); - if (taskEntity != null) { - taskEntity.entity = entity; - return taskEntity; - } - taskEntity = new DownloadTaskEntity(); - taskEntity.entity = entity; - taskEntity.headers = mTaskEntity.headers; - taskEntity.requestEnum = mTaskEntity.requestEnum; - taskEntity.redirectUrlKey = mTaskEntity.redirectUrlKey; - taskEntity.removeFile = mTaskEntity.removeFile; - taskEntity.groupName = mTaskEntity.key; - taskEntity.isGroupTask = true; - taskEntity.key = entity.getDownloadPath(); - taskEntity.save(); - return taskEntity; - } - - /** - * 子任务事件监听 - */ - private class ChildDownloadListener implements IDownloadListener { - - DownloadTaskEntity taskEntity; - DownloadEntity entity; - - long lastLen = 0; - - ChildDownloadListener(DownloadTaskEntity entity) { - this.taskEntity = entity; - this.entity = taskEntity.getEntity(); - lastLen = this.entity.getCurrentProgress(); - this.entity.setFailNum(0); - } - - @Override public void onPre() { - saveData(IEntity.STATE_PRE, -1); - } - - @Override public void onPostPre(long fileSize) { - entity.setFileSize(fileSize); - entity.setConvertFileSize(CommonUtil.formatFileSize(fileSize)); - saveData(IEntity.STATE_POST_PRE, -1); - } - - @Override public void onResume(long resumeLocation) { - saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); - lastLen = resumeLocation; - } - - @Override public void onStart(long startLocation) { - saveData(IEntity.STATE_POST_PRE, IEntity.STATE_RUNNING); - lastLen = startLocation; - } - - @Override public void onProgress(long currentLocation) { - long speed = currentLocation - lastLen; - mCurrentLocation += speed; - lastLen = currentLocation; - entity.setCurrentProgress(currentLocation); - handleSpeed(speed); - } - - @Override public void onStop(long stopLocation) { - saveData(IEntity.STATE_STOP, stopLocation); - handleSpeed(0); - mListener.onSubStop(entity); - } - - @Override public void onCancel() { - saveData(IEntity.STATE_CANCEL, -1); - handleSpeed(0); - mListener.onSubCancel(entity); - } - - @Override public void onComplete() { - saveData(IEntity.STATE_COMPLETE, entity.getFileSize()); - mCompleteNum++; - handleSpeed(0); - mListener.onSubComplete(entity); - //如果子任务完成的数量和总任务数一致,表示任务组任务已经完成 - if (mCompleteNum >= mTaskEntity.getEntity().getSubTask().size()) { - closeTimer(false); - mListener.onComplete(); - } else if (mCompleteNum + mFailNum >= mActualTaskNum) { - //如果子任务完成数量加上失败的数量和总任务数一致,则任务组停止下载 - closeTimer(false); - mListener.onComplete(); - } - } - - @Override public void onFail() { - entity.setFailNum(entity.getFailNum() + 1); - saveData(IEntity.STATE_FAIL, lastLen); - handleSpeed(0); - reTry(); - } - - /** - * 失败后重试下载,如果失败次数超过5次,不再重试 - */ - private void reTry() { - if (entity.getFailNum() < 5) { - reStartTask(); - } else { - mFailNum++; - mListener.onSubFail(entity); - //如果失败的任务数大于实际的下载任务数,任务组停止下载 - if (mFailNum >= mActualTaskNum) { - closeTimer(false); - mListener.onStop(mCurrentLocation); - } - } - } - - private void reStartTask() { - Timer timer = new Timer(); - timer.schedule(new TimerTask() { - @Override public void run() { - Downloader dt = mDownloaderMap.get(entity.getDownloadUrl()); - dt.startDownload(); - } - }, 3000); - } - - private void handleSpeed(long speed) { - entity.setSpeed(speed); - entity.setConvertSpeed(speed <= 0 ? "" : CommonUtil.formatFileSize(speed) + "/s"); - } - - private void saveData(int state, long location) { - entity.setState(state); - entity.setComplete(state == IEntity.STATE_COMPLETE); - if (entity.isComplete()) { - entity.setCompleteTime(System.currentTimeMillis()); - entity.setCurrentProgress(entity.getFileSize()); - } else if (location > 0) { - entity.setCurrentProgress(location); - } - entity.update(); - } - - @Override public void supportBreakpoint(boolean support) { - - } - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java index 89df24d3..69b71e23 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java @@ -96,8 +96,10 @@ class Downloader implements Runnable, IDownloadUtil { mConstance.THREAD_NUM = mThreadNum; handleNoSupportBreakpointDownload(); } else { - mThreadNum = isNewTask ? (mEntity.getFileSize() <= SUB_LEN ? 1 - : AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum()) : mRealThreadNum; + mThreadNum = isNewTask ? ( + mEntity.getFileSize() <= SUB_LEN || mTaskEntity.requestType == AbsTaskEntity.FTP_DIR ? 1 + : AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum()) + : mRealThreadNum; mConstance.THREAD_NUM = mThreadNum; mFixedThreadPool = Executors.newFixedThreadPool(mThreadNum); handleBreakpoint(); @@ -416,7 +418,7 @@ class Downloader implements Runnable, IDownloadUtil { private AbsThreadTask createThreadTask(SubThreadConfig config) { switch (mTaskEntity.requestType) { case AbsTaskEntity.FTP: - config.remotePath = mTaskEntity.remotePath; + case AbsTaskEntity.FTP_DIR: return new FtpThreadTask(mConstance, mListener, config); case AbsTaskEntity.HTTP: return new HttpThreadTask(mConstance, mListener, config); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java new file mode 100644 index 00000000..a81073f0 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.downloader; + +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import java.util.Set; + +/** + * Created by Aria.Lao on 2017/7/27. + * ftp文件夹下载工具 + */ +public class FtpDirDownloadUtil extends AbsGroupUtil { + public FtpDirDownloadUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity taskEntity) { + super(listener, taskEntity); + } + + @Override protected void onStart() { + super.onStart(); + if (mTaskEntity.getEntity().getFileSize() > 1) { + start(); + } else { + new FtpDirInfoThread(mTaskEntity, new OnFileInfoCallback() { + @Override public void onComplete(String url, int code) { + if (code >= 200 && code < 300) { + mTotalSize = mTaskEntity.getEntity().getFileSize(); + for (DownloadEntity entity : mTaskEntity.entity.getSubTask()) { + mExeMap.put(entity.getDownloadUrl(), createChildDownloadTask(entity)); + } + mActualTaskNum = mTaskEntity.entity.getSubTask().size(); + start(); + } + } + + @Override public void onFail(String url, String errorMsg) { + mListener.onFail(); + } + }).start(); + } + } + + private void start() { + int i = 0; + Set keys = mExeMap.keySet(); + for (String key : keys) { + DownloadTaskEntity taskEntity = mExeMap.get(key); + if (taskEntity != null) { + startChildDownload(taskEntity); + i++; + } + } + if (i == mExeMap.size()) startRunningFlow(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java index 451423fc..709e7d32 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java @@ -15,8 +15,12 @@ */ package com.arialyy.aria.core.download.downloader; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.util.CommonUtil; +import java.nio.charset.Charset; +import java.util.ArrayList; import org.apache.commons.net.ftp.FTPFile; /** @@ -24,6 +28,7 @@ import org.apache.commons.net.ftp.FTPFile; * 获取ftp文件夹信息 */ class FtpDirInfoThread extends AbsFtpInfoThread { + private long mSize = 0; FtpDirInfoThread(DownloadGroupTaskEntity taskEntity, OnFileInfoCallback callback) { super(taskEntity, callback); @@ -31,5 +36,33 @@ class FtpDirInfoThread extends AbsFtpInfoThread()); + } + if (mEntity.getSubTask() == null) { + mEntity.setSubTasks(new ArrayList()); + } + mEntity.getSubTask().add(entity); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java index f5fe280a..5cf1f324 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java @@ -51,10 +51,13 @@ class FtpThreadTask extends AbsThreadTask { + ",结束位置:" + mConfig.END_LOCATION + "】"); - String[] temp = mEntity.getDownloadUrl().split("/"); - String[] pp = temp[2].split(":"); + String url = mEntity.getDownloadUrl(); + String[] pp = url.split("/")[2].split(":"); + String serverIp = pp[0]; + int port = Integer.parseInt(pp[1]); + String remotePath = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()); client = new FTPClient(); - client.connect(pp[0], Integer.parseInt(pp[1])); + client.connect(serverIp, port); if (!TextUtils.isEmpty(mTaskEntity.account)) { client.login(mTaskEntity.userName, mTaskEntity.userPw); } else { @@ -66,12 +69,27 @@ class FtpThreadTask extends AbsThreadTask { failDownload(STATE.CURRENT_LOCATION, "无法连接到ftp服务器,错误码为:" + reply, null); return; } + String charSet = "UTF-8"; + // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码 + if (!TextUtils.isEmpty(mTaskEntity.charSet) || !FTPReply.isPositiveCompletion( + client.sendCommand("OPTS UTF8", "ON"))) { + charSet = mTaskEntity.charSet; + } + client.setControlEncoding(charSet); client.setDataTimeout(STATE.READ_TIME_OUT); client.enterLocalPassiveMode(); client.setFileType(FTP.BINARY_FILE_TYPE); client.setRestartOffset(mConfig.START_LOCATION); client.allocate(mBufSize); - is = client.retrieveFileStream(mConfig.remotePath); + is = client.retrieveFileStream( + new String(remotePath.getBytes(charSet), ConnectionHelp.SERVER_CHARSET)); + //发送第二次指令时,还需要再做一次判断 + reply = client.getReplyCode(); + if (!FTPReply.isPositivePreliminary(reply)) { + client.disconnect(); + failDownload(mChildCurrentLocation, "获取文件信息错误,错误码为:" + reply, null); + return; + } file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); file.seek(mConfig.START_LOCATION); byte[] buffer = new byte[mBufSize]; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SubThreadConfig.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SubThreadConfig.java index 077b1d50..f833474c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SubThreadConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SubThreadConfig.java @@ -22,7 +22,4 @@ class SubThreadConfig { String CONFIG_FILE_PATH; DownloadTaskEntity DOWNLOAD_TASK_ENTITY; boolean IS_SUPPORT_BREAK_POINT = true; - FTPClient client; - //远程地址 - String remotePath; } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java index 9d324ebf..29709aba 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java @@ -26,23 +26,29 @@ import java.util.Map; * Created by lyy on 2017/2/23. */ public abstract class AbsTaskEntity extends DbEntity { + /** + * HTTP下载 + */ public static final int HTTP = 0x11; + /** + * FTP当文件下载 + */ public static final int FTP = 0x12; - /** - * Task实体对应的key + * FTP文件夹下载,为避免登录过多,子任务由单线程进行处理 */ - @Primary public String key = ""; + public static final int FTP_DIR = 0x13; /** - * FTP服务器文件或文件夹路径 + * Task实体对应的key */ - public String remotePath; + @Primary public String key = ""; /** * 账号和密码 */ - @Ignore public String userName, userPw, account; + @Ignore public String userName, userPw, account, serverIp; + @Ignore public int port; /** * 请求类型 diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java b/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java index 70bee951..117fe6c0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java @@ -33,63 +33,63 @@ public interface ITask { /** * 唯一标识符,DownloadTask 为下载地址,UploadTask 为文件路径 */ - public String getKey(); + String getKey(); /** * 任务是否正在执行 * * @return true,正在执行; */ - public boolean isRunning(); + boolean isRunning(); /** * 获取信息实体 */ - public ENTITY getEntity(); + ENTITY getEntity(); - public void start(); + void start(); - public void stop(); + void stop(); - public void cancel(); + void cancel(); /** * 原始byte速度 */ - public long getSpeed(); + long getSpeed(); /** * 转换单位后的速度 */ - public String getConvertSpeed(); + String getConvertSpeed(); /** * 获取百分比进度 */ - public int getPercent(); + int getPercent(); /** * 原始文件byte长度 */ - public long getFileSize(); + long getFileSize(); /** * 转换单位后的文件长度 */ - public String getConvertFileSize(); + String getConvertFileSize(); /** * 获取当前进度 */ - public long getCurrentProgress(); + long getCurrentProgress(); /** * 获取单位转换后的进度 * * @return 返回 3mb */ - public String getConvertCurrentProgress(); + String getConvertCurrentProgress(); - public void setTargetName(String targetName); + void setTargetName(String targetName); } diff --git a/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java index 90a1d46a..20e22607 100644 --- a/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java @@ -36,7 +36,8 @@ import java.io.File; */ public class FtpDownloadActivity extends BaseActivity { //private final String URL = "ftp://172.18.104.129:21/haha/large.rar"; - private final String URL = "ftp://172.18.104.129:21/cd.mp3"; + //private final String URL = "ftp://172.18.104.129:21/haha/large.rar"; + private final String URL = "ftp://172.18.104.129:21/haha/很大的文件_v100.rar"; @Override protected void init(Bundle savedInstanceState) { super.init(savedInstanceState); @@ -61,7 +62,6 @@ public class FtpDownloadActivity extends BaseActivity