parent
b987c3fcfa
commit
665cc48867
@ -0,0 +1,195 @@ |
|||||||
|
/* |
||||||
|
* 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 android.os.Build; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.math.BigDecimal; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/1/18. |
||||||
|
* 下载线程 |
||||||
|
*/ |
||||||
|
abstract class AbsThreadTask implements Runnable { |
||||||
|
private final String TAG = "AbsThreadTask"; |
||||||
|
long mChildCurrentLocation = 0, mSleepTime = 0; |
||||||
|
int mBufSize; |
||||||
|
String mConfigFPath; |
||||||
|
SubThreadConfig mConfig; |
||||||
|
IDownloadListener mListener; |
||||||
|
StateConstance STATE; |
||||||
|
DownloadEntity mEntity; |
||||||
|
DownloadTaskEntity mTaskEntity; |
||||||
|
|
||||||
|
AbsThreadTask(StateConstance constance, IDownloadListener listener, |
||||||
|
SubThreadConfig downloadInfo) { |
||||||
|
AriaManager manager = AriaManager.getInstance(AriaManager.APP); |
||||||
|
STATE = constance; |
||||||
|
STATE.CONNECT_TIME_OUT = manager.getDownloadConfig().getConnectTimeOut(); |
||||||
|
STATE.READ_TIME_OUT = manager.getDownloadConfig().getIOTimeOut(); |
||||||
|
mListener = listener; |
||||||
|
mConfig = downloadInfo; |
||||||
|
mTaskEntity = mConfig.DOWNLOAD_TASK_ENTITY; |
||||||
|
mEntity = mTaskEntity.getEntity(); |
||||||
|
if (mConfig.IS_SUPPORT_BREAK_POINT) { |
||||||
|
mConfigFPath = downloadInfo.CONFIG_FILE_PATH; |
||||||
|
} |
||||||
|
mBufSize = manager.getDownloadConfig().getBuffSize(); |
||||||
|
setMaxSpeed(AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMsxSpeed()); |
||||||
|
} |
||||||
|
|
||||||
|
void setMaxSpeed(double maxSpeed) { |
||||||
|
if (-0.9999 < maxSpeed && maxSpeed < 0.00001) { |
||||||
|
mSleepTime = 0; |
||||||
|
} else { |
||||||
|
BigDecimal db = new BigDecimal( |
||||||
|
((mBufSize / 1024) * (filterVersion() ? 1 : STATE.THREAD_NUM) / maxSpeed) * 1000); |
||||||
|
mSleepTime = db.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean filterVersion() { |
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 停止下载 |
||||||
|
*/ |
||||||
|
void stop() { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
try { |
||||||
|
if (mConfig.IS_SUPPORT_BREAK_POINT) { |
||||||
|
STATE.STOP_NUM++; |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】thread__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__停止, stop location ==> " |
||||||
|
+ mChildCurrentLocation); |
||||||
|
writeConfig(false, mChildCurrentLocation); |
||||||
|
if (STATE.isStop()) { |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已停止"); |
||||||
|
STATE.isDownloading = false; |
||||||
|
mListener.onStop(STATE.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已停止"); |
||||||
|
STATE.isDownloading = false; |
||||||
|
mListener.onStop(STATE.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载中 |
||||||
|
*/ |
||||||
|
void progress(long len) { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
mChildCurrentLocation += len; |
||||||
|
STATE.CURRENT_LOCATION += len; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 取消下载 |
||||||
|
*/ |
||||||
|
void cancel() { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
if (mConfig.IS_SUPPORT_BREAK_POINT) { |
||||||
|
STATE.CANCEL_NUM++; |
||||||
|
Log.d(TAG, |
||||||
|
"任务【" + mConfig.TEMP_FILE.getName() + "】thread__" + mConfig.THREAD_ID + "__取消下载"); |
||||||
|
if (STATE.isCancel()) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
if (configFile.exists()) { |
||||||
|
configFile.delete(); |
||||||
|
} |
||||||
|
if (mConfig.TEMP_FILE.exists()) { |
||||||
|
mConfig.TEMP_FILE.delete(); |
||||||
|
} |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已取消"); |
||||||
|
STATE.isDownloading = false; |
||||||
|
mListener.onCancel(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已取消"); |
||||||
|
STATE.isDownloading = false; |
||||||
|
mListener.onCancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 下载失败 |
||||||
|
*/ |
||||||
|
void failDownload(long currentLocation, String msg, Exception ex) { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
try { |
||||||
|
STATE.FAIL_NUM++; |
||||||
|
STATE.isDownloading = false; |
||||||
|
STATE.isStop = true; |
||||||
|
if (ex != null) { |
||||||
|
Log.e(TAG, msg + "\n" + CommonUtil.getPrintException(ex)); |
||||||
|
} |
||||||
|
if (mConfig.IS_SUPPORT_BREAK_POINT) { |
||||||
|
writeConfig(false, currentLocation); |
||||||
|
if (STATE.isFail()) { |
||||||
|
Log.e(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】下载失败"); |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.e(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】下载失败"); |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将记录写入到配置文件 |
||||||
|
*/ |
||||||
|
void writeConfig(boolean isComplete, long record) throws IOException { |
||||||
|
synchronized (AriaManager.LOCK) { |
||||||
|
String key = null, value = null; |
||||||
|
if (0 < record && record < mConfig.END_LOCATION) { |
||||||
|
key = mConfig.TEMP_FILE.getName() + "_record_" + mConfig.THREAD_ID; |
||||||
|
value = String.valueOf(record); |
||||||
|
} else if (record >= mConfig.END_LOCATION || isComplete) { |
||||||
|
key = mConfig.TEMP_FILE.getName() + "_state_" + mConfig.THREAD_ID; |
||||||
|
value = "1"; |
||||||
|
} |
||||||
|
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
Properties pro = CommonUtil.loadConfig(configFile); |
||||||
|
pro.setProperty(key, value); |
||||||
|
CommonUtil.saveConfig(configFile, pro); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,24 +0,0 @@ |
|||||||
package com.arialyy.aria.core.download.downloader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.DownloadTaskEntity; |
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子线程下载信息类 |
|
||||||
*/ |
|
||||||
public class ChildThreadConfigEntity { |
|
||||||
//线程Id
|
|
||||||
public int THREAD_ID; |
|
||||||
//下载文件大小
|
|
||||||
public long FILE_SIZE; |
|
||||||
//子线程启动下载位置
|
|
||||||
public long START_LOCATION; |
|
||||||
//子线程结束下载位置
|
|
||||||
public long END_LOCATION; |
|
||||||
//下载路径
|
|
||||||
public File TEMP_FILE; |
|
||||||
public String DOWNLOAD_URL; |
|
||||||
public String CONFIG_FILE_PATH; |
|
||||||
public DownloadTaskEntity DOWNLOAD_TASK_ENTITY; |
|
||||||
public boolean IS_SUPPORT_BREAK_POINT = true; |
|
||||||
} |
|
@ -0,0 +1,122 @@ |
|||||||
|
/* |
||||||
|
* 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 android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.download.DownloadEntity; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.IOException; |
||||||
|
import org.apache.commons.net.ftp.FTP; |
||||||
|
import org.apache.commons.net.ftp.FTPClient; |
||||||
|
import org.apache.commons.net.ftp.FTPFile; |
||||||
|
import org.apache.commons.net.ftp.FTPReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/25. |
||||||
|
* 获取ftp文件信息 |
||||||
|
*/ |
||||||
|
class FtpFileInfoThread implements Runnable { |
||||||
|
|
||||||
|
private final String TAG = "HttpFileInfoThread"; |
||||||
|
private DownloadEntity mEntity; |
||||||
|
private DownloadTaskEntity mTaskEntity; |
||||||
|
private int mConnectTimeOut; |
||||||
|
private OnFileInfoCallback mCallback; |
||||||
|
|
||||||
|
FtpFileInfoThread(DownloadTaskEntity taskEntity, OnFileInfoCallback callback) { |
||||||
|
this.mTaskEntity = taskEntity; |
||||||
|
mEntity = taskEntity.getEntity(); |
||||||
|
mConnectTimeOut = |
||||||
|
AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getConnectTimeOut(); |
||||||
|
mCallback = callback; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
FTPClient client = null; |
||||||
|
try { |
||||||
|
client = new FTPClient(); |
||||||
|
//ip和端口
|
||||||
|
String[] temp = mEntity.getDownloadUrl().split("/"); |
||||||
|
String[] pp = temp[2].split(":"); |
||||||
|
//String dir = temp[temp.length - 2];
|
||||||
|
String fileName = temp[temp.length - 1]; |
||||||
|
client.connect(pp[0], Integer.parseInt(pp[1])); |
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.account)) { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw); |
||||||
|
} else { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw, mTaskEntity.account); |
||||||
|
} |
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
failDownload("无法连接到ftp服务器,错误码为:" + reply); |
||||||
|
return; |
||||||
|
} |
||||||
|
client.setDataTimeout(mConnectTimeOut); |
||||||
|
client.enterLocalPassiveMode(); |
||||||
|
client.setFileType(FTP.BINARY_FILE_TYPE); |
||||||
|
FTPFile[] files = |
||||||
|
client.listFiles(CommonUtil.strCharSetConvert(fileName, mTaskEntity.charSet)); |
||||||
|
long size = getFileSize(files, client, fileName); |
||||||
|
mEntity.setFileSize(size); |
||||||
|
mTaskEntity.code = reply; |
||||||
|
mEntity.update(); |
||||||
|
mTaskEntity.update(); |
||||||
|
mCallback.onComplete(mEntity.getDownloadUrl(), reply); |
||||||
|
} catch (IOException e) { |
||||||
|
failDownload(e.getMessage()); |
||||||
|
} finally { |
||||||
|
if (client != null) { |
||||||
|
try { |
||||||
|
client.logout(); |
||||||
|
client.disconnect(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 遍历FTP服务器上对应文件或文件夹大小 |
||||||
|
* |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
private long getFileSize(FTPFile[] files, FTPClient client, String dirName) throws IOException { |
||||||
|
long size = 0; |
||||||
|
String path = dirName + "/"; |
||||||
|
for (FTPFile file : files) { |
||||||
|
if (file.isFile()) { |
||||||
|
size += file.getSize(); |
||||||
|
} else { |
||||||
|
size += getFileSize(client.listFiles( |
||||||
|
CommonUtil.strCharSetConvert(path + file.getName(), mTaskEntity.charSet)), client, |
||||||
|
path + file.getName()); |
||||||
|
} |
||||||
|
} |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
private void failDownload(String errorMsg) { |
||||||
|
Log.e(TAG, errorMsg); |
||||||
|
if (mCallback != null) { |
||||||
|
mCallback.onFail(mEntity.getDownloadUrl(), errorMsg); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
/* |
||||||
|
* 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 android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import java.io.BufferedInputStream; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import org.apache.commons.net.ftp.FTP; |
||||||
|
import org.apache.commons.net.ftp.FTPClient; |
||||||
|
import org.apache.commons.net.ftp.FTPFile; |
||||||
|
import org.apache.commons.net.ftp.FTPReply; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/24. |
||||||
|
* Ftp下载任务 |
||||||
|
*/ |
||||||
|
class FtpThreadTask extends AbsThreadTask { |
||||||
|
private final String TAG = "FtpThreadTask"; |
||||||
|
|
||||||
|
FtpThreadTask(StateConstance constance, IDownloadListener listener, |
||||||
|
SubThreadConfig downloadInfo) { |
||||||
|
super(constance, listener, downloadInfo); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
FTPClient client = null; |
||||||
|
InputStream is = null; |
||||||
|
BufferedRandomAccessFile file = null; |
||||||
|
try { |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】线程__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__开始下载【开始位置 : " |
||||||
|
+ mConfig.START_LOCATION |
||||||
|
+ ",结束位置:" |
||||||
|
+ mConfig.END_LOCATION |
||||||
|
+ "】"); |
||||||
|
client = new FTPClient(); |
||||||
|
//ip和端口
|
||||||
|
String[] temp = mEntity.getDownloadUrl().split("/"); |
||||||
|
String[] pp = temp[2].split(":"); |
||||||
|
//String dir = temp[temp.length - 2];
|
||||||
|
String fileName = temp[temp.length - 1]; |
||||||
|
client.connect(pp[0], Integer.parseInt(pp[1])); |
||||||
|
if (!TextUtils.isEmpty(mTaskEntity.account)) { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw); |
||||||
|
} else { |
||||||
|
client.login(mTaskEntity.userName, mTaskEntity.userPw, mTaskEntity.account); |
||||||
|
} |
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositiveCompletion(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
failDownload(STATE.CURRENT_LOCATION, "无法连接到ftp服务器,错误码为:" + reply, null); |
||||||
|
return; |
||||||
|
} |
||||||
|
client.setDataTimeout(STATE.READ_TIME_OUT); |
||||||
|
client.enterLocalPassiveMode(); |
||||||
|
client.setFileType(FTP.BINARY_FILE_TYPE); |
||||||
|
client.setRestartOffset(mConfig.START_LOCATION); |
||||||
|
is = client.retrieveFileStream(fileName); |
||||||
|
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); |
||||||
|
file.seek(mConfig.START_LOCATION); |
||||||
|
byte[] buffer = new byte[mBufSize]; |
||||||
|
int len; |
||||||
|
//当前子线程的下载位置
|
||||||
|
mChildCurrentLocation = mConfig.START_LOCATION; |
||||||
|
//TODO: 2017/7/25 隐藏问题,速度太快(20m/s)或文件太小,秒下,会出现 ftp Connection reset by peer 问题
|
||||||
|
while ((len = is.read(buffer)) != -1) { |
||||||
|
if (STATE.isCancel) break; |
||||||
|
if (STATE.isStop) break; |
||||||
|
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
||||||
|
if (mChildCurrentLocation + len >= mConfig.END_LOCATION) { |
||||||
|
len = (int) (mConfig.END_LOCATION - mChildCurrentLocation); |
||||||
|
file.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
break; |
||||||
|
} else { |
||||||
|
file.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
} |
||||||
|
} |
||||||
|
if (STATE.isCancel || STATE.isStop) return; |
||||||
|
if (client.completePendingCommand()) { |
||||||
|
Log.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__下载完毕"); |
||||||
|
writeConfig(true, 1); |
||||||
|
STATE.COMPLETE_THREAD_NUM++; |
||||||
|
if (STATE.isComplete()) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
if (configFile.exists()) { |
||||||
|
configFile.delete(); |
||||||
|
} |
||||||
|
STATE.isDownloading = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
file.close(); |
||||||
|
is.close(); |
||||||
|
Log.d(TAG, "SUCCESS"); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
failDownload(mChildCurrentLocation, "下载失败【" + mConfig.DOWNLOAD_URL + "】", e); |
||||||
|
} catch (Exception e) { |
||||||
|
failDownload(mChildCurrentLocation, "获取流失败", e); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (client != null && client.isConnected()) { |
||||||
|
//client.logout();
|
||||||
|
client.disconnect(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
/* |
||||||
|
* 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 android.util.Log; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.net.HttpURLConnection; |
||||||
|
import java.net.MalformedURLException; |
||||||
|
import java.net.URL; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/1/18. |
||||||
|
* 下载线程 |
||||||
|
*/ |
||||||
|
final class HttpThreadTask extends AbsThreadTask { |
||||||
|
private final String TAG = "HttpThreadTask"; |
||||||
|
|
||||||
|
HttpThreadTask(StateConstance constance, IDownloadListener listener, |
||||||
|
SubThreadConfig downloadInfo) { |
||||||
|
super(constance, listener, downloadInfo); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
HttpURLConnection conn = null; |
||||||
|
InputStream is = null; |
||||||
|
BufferedRandomAccessFile file = null; |
||||||
|
try { |
||||||
|
URL url = new URL(mConfig.DOWNLOAD_URL); |
||||||
|
conn = ConnectionHelp.handleConnection(url); |
||||||
|
if (mConfig.IS_SUPPORT_BREAK_POINT) { |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】线程__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__开始下载【开始位置 : " |
||||||
|
+ mConfig.START_LOCATION |
||||||
|
+ ",结束位置:" |
||||||
|
+ mConfig.END_LOCATION |
||||||
|
+ "】"); |
||||||
|
//在头里面请求下载开始位置和结束位置
|
||||||
|
conn.setRequestProperty("Range", |
||||||
|
"bytes=" + mConfig.START_LOCATION + "-" + (mConfig.END_LOCATION - 1)); |
||||||
|
} else { |
||||||
|
Log.w(TAG, "该下载不支持断点"); |
||||||
|
} |
||||||
|
conn = ConnectionHelp.setConnectParam(mConfig.DOWNLOAD_TASK_ENTITY, conn); |
||||||
|
conn.setConnectTimeout(STATE.CONNECT_TIME_OUT); |
||||||
|
conn.setReadTimeout(STATE.READ_TIME_OUT); //设置读取流的等待时间,必须设置该参数
|
||||||
|
is = conn.getInputStream(); |
||||||
|
//创建可设置位置的文件
|
||||||
|
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); |
||||||
|
//设置每条线程写入文件的位置
|
||||||
|
file.seek(mConfig.START_LOCATION); |
||||||
|
byte[] buffer = new byte[mBufSize]; |
||||||
|
int len; |
||||||
|
//当前子线程的下载位置
|
||||||
|
mChildCurrentLocation = mConfig.START_LOCATION; |
||||||
|
while ((len = is.read(buffer)) != -1) { |
||||||
|
if (STATE.isCancel) break; |
||||||
|
if (STATE.isStop) break; |
||||||
|
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
||||||
|
file.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
} |
||||||
|
if (STATE.isCancel || STATE.isStop) return; |
||||||
|
//支持断点的处理
|
||||||
|
if (mConfig.IS_SUPPORT_BREAK_POINT) { |
||||||
|
Log.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__下载完毕"); |
||||||
|
writeConfig(true, 1); |
||||||
|
STATE.COMPLETE_THREAD_NUM++; |
||||||
|
if (STATE.isComplete()) { |
||||||
|
File configFile = new File(mConfigFPath); |
||||||
|
if (configFile.exists()) { |
||||||
|
configFile.delete(); |
||||||
|
} |
||||||
|
STATE.isDownloading = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.i(TAG, "下载任务完成"); |
||||||
|
STATE.isDownloading = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
} catch (MalformedURLException e) { |
||||||
|
failDownload(mChildCurrentLocation, "下载链接异常", e); |
||||||
|
} catch (IOException e) { |
||||||
|
failDownload(mChildCurrentLocation, "下载失败【" + mConfig.DOWNLOAD_URL + "】", e); |
||||||
|
} catch (Exception e) { |
||||||
|
failDownload(mChildCurrentLocation, "获取流失败", e); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (file != null) { |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
if (is != null) { |
||||||
|
is.close(); |
||||||
|
} |
||||||
|
if (conn != null) { |
||||||
|
conn.disconnect(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.arialyy.aria.core.download.downloader; |
||||||
|
|
||||||
|
interface OnFileInfoCallback { |
||||||
|
/** |
||||||
|
* 处理完成 |
||||||
|
* |
||||||
|
* @param code 状态码 |
||||||
|
*/ |
||||||
|
void onComplete(String url, int code); |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求失败 |
||||||
|
* |
||||||
|
* @param errorMsg 错误信息 |
||||||
|
*/ |
||||||
|
void onFail(String url, String errorMsg); |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.arialyy.aria.core.download.downloader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* 子线程下载信息类 |
||||||
|
*/ |
||||||
|
class SubThreadConfig { |
||||||
|
//线程Id
|
||||||
|
int THREAD_ID; |
||||||
|
//下载文件大小
|
||||||
|
long FILE_SIZE; |
||||||
|
//子线程启动下载位置
|
||||||
|
long START_LOCATION; |
||||||
|
//子线程结束下载位置
|
||||||
|
long END_LOCATION; |
||||||
|
//下载路径
|
||||||
|
File TEMP_FILE; |
||||||
|
String DOWNLOAD_URL; |
||||||
|
String CONFIG_FILE_PATH; |
||||||
|
DownloadTaskEntity DOWNLOAD_TASK_ENTITY; |
||||||
|
boolean IS_SUPPORT_BREAK_POINT = true; |
||||||
|
} |
@ -1,93 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.ftp; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.download.DownloadTaskEntity; |
|
||||||
import com.arialyy.aria.core.download.downloader.IDownloadListener; |
|
||||||
import com.arialyy.aria.core.download.downloader.IDownloadUtil; |
|
||||||
import com.arialyy.aria.util.BufferedRandomAccessFile; |
|
||||||
import java.io.File; |
|
||||||
import java.io.FileOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import org.apache.commons.net.ftp.FTP; |
|
||||||
import org.apache.commons.net.ftp.FTPClient; |
|
||||||
import org.apache.commons.net.ftp.FTPFile; |
|
||||||
import org.apache.commons.net.ftp.FTPFileEntryParser; |
|
||||||
import org.apache.commons.net.ftp.FTPListParseEngine; |
|
||||||
import org.apache.commons.net.ftp.FTPReply; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by Aria.Lao on 2017/7/21. |
|
||||||
*/ |
|
||||||
public class FtpDownloadUtil implements IDownloadUtil, Runnable { |
|
||||||
|
|
||||||
private IDownloadListener mListener; |
|
||||||
private DownloadTaskEntity mTaskEntity; |
|
||||||
private DownloadEntity mEntity; |
|
||||||
|
|
||||||
public FtpDownloadUtil(DownloadTaskEntity entity, IDownloadListener downloadListener) { |
|
||||||
mTaskEntity = entity; |
|
||||||
mListener = downloadListener; |
|
||||||
mEntity = mTaskEntity.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isDownloading() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancelDownload() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stopDownload() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void startDownload() { |
|
||||||
mListener.onPre(); |
|
||||||
new Thread(this).start(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void resumeDownload() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void setMaxSpeed(double maxSpeed) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
private void failDownload(String msg) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
private void test() throws IOException { |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,281 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.ftp; |
|
||||||
|
|
||||||
import android.os.Build; |
|
||||||
import android.text.TextUtils; |
|
||||||
import android.util.Log; |
|
||||||
import com.arialyy.aria.core.AriaManager; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.download.DownloadTaskEntity; |
|
||||||
import com.arialyy.aria.core.download.downloader.ChildThreadConfigEntity; |
|
||||||
import com.arialyy.aria.core.download.downloader.IDownloadListener; |
|
||||||
import com.arialyy.aria.core.download.downloader.StateConstance; |
|
||||||
import com.arialyy.aria.util.BufferedRandomAccessFile; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.math.BigDecimal; |
|
||||||
import java.util.Properties; |
|
||||||
import org.apache.commons.net.ftp.FTP; |
|
||||||
import org.apache.commons.net.ftp.FTPClient; |
|
||||||
import org.apache.commons.net.ftp.FTPFile; |
|
||||||
import org.apache.commons.net.ftp.FTPReply; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by Aria.Lao on 2017/7/24. |
|
||||||
* Ftp下载任务 |
|
||||||
*/ |
|
||||||
class FtpThreadTask implements Runnable { |
|
||||||
private final String TAG = "FtpThreadTask"; |
|
||||||
private ChildThreadConfigEntity mConfig; |
|
||||||
private String mConfigFPath; |
|
||||||
private long mChildCurrentLocation = 0; |
|
||||||
private int mBufSize; |
|
||||||
private IDownloadListener mListener; |
|
||||||
private StateConstance STATE; |
|
||||||
private long mSleepTime = 0; |
|
||||||
private DownloadTaskEntity mTaskEntity; |
|
||||||
private DownloadEntity mEntity; |
|
||||||
|
|
||||||
FtpThreadTask(StateConstance constance, IDownloadListener listener, |
|
||||||
ChildThreadConfigEntity downloadInfo) { |
|
||||||
AriaManager manager = AriaManager.getInstance(AriaManager.APP); |
|
||||||
STATE = constance; |
|
||||||
STATE.CONNECT_TIME_OUT = manager.getDownloadConfig().getConnectTimeOut(); |
|
||||||
STATE.READ_TIME_OUT = manager.getDownloadConfig().getIOTimeOut(); |
|
||||||
mListener = listener; |
|
||||||
this.mConfig = downloadInfo; |
|
||||||
mConfigFPath = downloadInfo.CONFIG_FILE_PATH; |
|
||||||
mTaskEntity = mConfig.DOWNLOAD_TASK_ENTITY; |
|
||||||
mEntity = mTaskEntity.getEntity(); |
|
||||||
mBufSize = manager.getDownloadConfig().getBuffSize(); |
|
||||||
setMaxSpeed(AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMsxSpeed()); |
|
||||||
} |
|
||||||
|
|
||||||
void setMaxSpeed(double maxSpeed) { |
|
||||||
if (-0.9999 < maxSpeed && maxSpeed < 0.00001) { |
|
||||||
mSleepTime = 0; |
|
||||||
} else { |
|
||||||
BigDecimal db = new BigDecimal( |
|
||||||
((mBufSize / 1024) * (filterVersion() ? 1 : STATE.THREAD_NUM) / maxSpeed) * 1000); |
|
||||||
mSleepTime = db.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private boolean filterVersion() { |
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
FTPClient client = null; |
|
||||||
InputStream is = null; |
|
||||||
BufferedRandomAccessFile file = null; |
|
||||||
try { |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfig.TEMP_FILE.getName() |
|
||||||
+ "】线程__" |
|
||||||
+ mConfig.THREAD_ID |
|
||||||
+ "__开始下载【开始位置 : " |
|
||||||
+ mConfig.START_LOCATION |
|
||||||
+ ",结束位置:" |
|
||||||
+ mConfig.END_LOCATION |
|
||||||
+ "】"); |
|
||||||
client = new FTPClient(); |
|
||||||
//ip和端口
|
|
||||||
String[] temp = mEntity.getDownloadUrl().split("/"); |
|
||||||
String[] pp = temp[2].split(":"); |
|
||||||
String dir = temp[temp.length - 2]; |
|
||||||
String fileName = temp[temp.length - 1]; |
|
||||||
client.connect(pp[0], Integer.parseInt(pp[1])); |
|
||||||
if (!TextUtils.isEmpty(mTaskEntity.account)) { |
|
||||||
client.login(mTaskEntity.userName, mTaskEntity.userPw); |
|
||||||
} else { |
|
||||||
client.login(mTaskEntity.userName, mTaskEntity.userPw, mTaskEntity.account); |
|
||||||
} |
|
||||||
int reply = client.getReplyCode(); |
|
||||||
if (!FTPReply.isPositiveCompletion(reply)) { |
|
||||||
client.disconnect(); |
|
||||||
failDownload(STATE.CURRENT_LOCATION, "无法连接到ftp服务器,错误码为:" + reply, null); |
|
||||||
return; |
|
||||||
} |
|
||||||
client.enterLocalPassiveMode(); |
|
||||||
client.setFileType(FTP.BINARY_FILE_TYPE); |
|
||||||
FTPFile[] files = client.listFiles(fileName); |
|
||||||
files[0].getSize(); |
|
||||||
client.setRestartOffset(mConfig.START_LOCATION); |
|
||||||
is = client.retrieveFileStream(fileName); |
|
||||||
//创建可设置位置的文件
|
|
||||||
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); |
|
||||||
//设置每条线程写入文件的位置
|
|
||||||
file.seek(mConfig.START_LOCATION); |
|
||||||
byte[] buffer = new byte[mBufSize]; |
|
||||||
int len; |
|
||||||
//当前子线程的下载位置
|
|
||||||
mChildCurrentLocation = mConfig.START_LOCATION; |
|
||||||
while ((len = is.read(buffer)) != -1) { |
|
||||||
if (STATE.isCancel) break; |
|
||||||
if (STATE.isStop) break; |
|
||||||
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
|
||||||
file.write(buffer, 0, len); |
|
||||||
progress(len); |
|
||||||
} |
|
||||||
if (STATE.isCancel) return; |
|
||||||
//停止状态不需要删除记录文件
|
|
||||||
if (STATE.isStop) return; |
|
||||||
//支持断点的处理
|
|
||||||
if (mConfig.IS_SUPPORT_BREAK_POINT) { |
|
||||||
Log.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__下载完毕"); |
|
||||||
writeConfig(true, 1); |
|
||||||
STATE.COMPLETE_THREAD_NUM++; |
|
||||||
if (STATE.isComplete()) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
if (configFile.exists()) { |
|
||||||
configFile.delete(); |
|
||||||
} |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onComplete(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.i(TAG, "下载任务完成"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onComplete(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
failDownload(mChildCurrentLocation, "下载失败【" + mConfig.DOWNLOAD_URL + "】", e); |
|
||||||
} catch (Exception e) { |
|
||||||
failDownload(mChildCurrentLocation, "获取流失败", e); |
|
||||||
} finally { |
|
||||||
try { |
|
||||||
if (file != null) { |
|
||||||
file.close(); |
|
||||||
} |
|
||||||
if (is != null) { |
|
||||||
is.close(); |
|
||||||
} |
|
||||||
if (client != null) { |
|
||||||
client.logout(); |
|
||||||
client.disconnect(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止下载 |
|
||||||
*/ |
|
||||||
protected void stop() { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
try { |
|
||||||
STATE.STOP_NUM++; |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfig.TEMP_FILE.getName() |
|
||||||
+ "】thread__" |
|
||||||
+ mConfig.THREAD_ID |
|
||||||
+ "__停止, stop location ==> " |
|
||||||
+ mChildCurrentLocation); |
|
||||||
writeConfig(false, mChildCurrentLocation); |
|
||||||
if (STATE.isStop()) { |
|
||||||
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已停止"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onStop(STATE.CURRENT_LOCATION); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 下载中 |
|
||||||
*/ |
|
||||||
private void progress(long len) { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
mChildCurrentLocation += len; |
|
||||||
STATE.CURRENT_LOCATION += len; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 取消下载 |
|
||||||
*/ |
|
||||||
protected void cancel() { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
STATE.CANCEL_NUM++; |
|
||||||
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】thread__" + mConfig.THREAD_ID + "__取消下载"); |
|
||||||
if (STATE.isCancel()) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
if (configFile.exists()) { |
|
||||||
configFile.delete(); |
|
||||||
} |
|
||||||
if (mConfig.TEMP_FILE.exists()) { |
|
||||||
mConfig.TEMP_FILE.delete(); |
|
||||||
} |
|
||||||
Log.d(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】已取消"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onCancel(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 下载失败 |
|
||||||
*/ |
|
||||||
private void failDownload(long currentLocation, String msg, Exception ex) { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
try { |
|
||||||
STATE.FAIL_NUM++; |
|
||||||
STATE.isDownloading = false; |
|
||||||
STATE.isStop = true; |
|
||||||
if (ex != null) { |
|
||||||
Log.e(TAG, msg + "\n" + CommonUtil.getPrintException(ex)); |
|
||||||
} |
|
||||||
writeConfig(false, currentLocation); |
|
||||||
if (STATE.isFail()) { |
|
||||||
Log.e(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】下载失败"); |
|
||||||
mListener.onFail(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 将记录写入到配置文件 |
|
||||||
*/ |
|
||||||
private void writeConfig(boolean isComplete, long record) throws IOException { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
String key = null, value = null; |
|
||||||
if (0 < record && record < mConfig.END_LOCATION) { |
|
||||||
key = mConfig.TEMP_FILE.getName() + "_record_" + mConfig.THREAD_ID; |
|
||||||
value = String.valueOf(record); |
|
||||||
} else if (record >= mConfig.END_LOCATION || isComplete) { |
|
||||||
key = mConfig.TEMP_FILE.getName() + "_state_" + mConfig.THREAD_ID; |
|
||||||
value = "1"; |
|
||||||
} |
|
||||||
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
Properties pro = CommonUtil.loadConfig(configFile); |
|
||||||
pro.setProperty(key, value); |
|
||||||
CommonUtil.saveConfig(configFile, pro); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,291 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.http; |
|
||||||
|
|
||||||
import android.os.Build; |
|
||||||
import android.text.TextUtils; |
|
||||||
import android.util.Log; |
|
||||||
import com.arialyy.aria.core.AriaManager; |
|
||||||
import com.arialyy.aria.core.download.downloader.ChildThreadConfigEntity; |
|
||||||
import com.arialyy.aria.core.download.downloader.IDownloadListener; |
|
||||||
import com.arialyy.aria.core.download.downloader.StateConstance; |
|
||||||
import com.arialyy.aria.util.BufferedRandomAccessFile; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.math.BigDecimal; |
|
||||||
import java.net.HttpURLConnection; |
|
||||||
import java.net.MalformedURLException; |
|
||||||
import java.net.URL; |
|
||||||
import java.util.Properties; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2017/1/18. |
|
||||||
* 下载线程 |
|
||||||
*/ |
|
||||||
final class HttpThreadTask implements Runnable { |
|
||||||
private static final String TAG = "HttpThreadTask"; |
|
||||||
private ChildThreadConfigEntity mConfigEntity; |
|
||||||
private String mConfigFPath; |
|
||||||
private long mChildCurrentLocation = 0; |
|
||||||
private int mBufSize; |
|
||||||
private IDownloadListener mListener; |
|
||||||
private StateConstance STATE; |
|
||||||
private long mSleepTime = 0; |
|
||||||
|
|
||||||
HttpThreadTask(StateConstance constance, IDownloadListener listener, |
|
||||||
ChildThreadConfigEntity downloadInfo) { |
|
||||||
AriaManager manager = AriaManager.getInstance(AriaManager.APP); |
|
||||||
STATE = constance; |
|
||||||
STATE.CONNECT_TIME_OUT = manager.getDownloadConfig().getConnectTimeOut(); |
|
||||||
STATE.READ_TIME_OUT = manager.getDownloadConfig().getIOTimeOut(); |
|
||||||
mListener = listener; |
|
||||||
this.mConfigEntity = downloadInfo; |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
mConfigFPath = downloadInfo.CONFIG_FILE_PATH; |
|
||||||
} |
|
||||||
mBufSize = manager.getDownloadConfig().getBuffSize(); |
|
||||||
setMaxSpeed(AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getMsxSpeed()); |
|
||||||
} |
|
||||||
|
|
||||||
void setMaxSpeed(double maxSpeed) { |
|
||||||
if (-0.9999 < maxSpeed && maxSpeed < 0.00001) { |
|
||||||
mSleepTime = 0; |
|
||||||
} else { |
|
||||||
BigDecimal db = new BigDecimal( |
|
||||||
((mBufSize / 1024) * (filterVersion() ? 1 : STATE.THREAD_NUM) / maxSpeed) * 1000); |
|
||||||
mSleepTime = db.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private boolean filterVersion() { |
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
HttpURLConnection conn = null; |
|
||||||
InputStream is = null; |
|
||||||
BufferedRandomAccessFile file = null; |
|
||||||
try { |
|
||||||
URL url = new URL(mConfigEntity.DOWNLOAD_URL); |
|
||||||
conn = ConnectionHelp.handleConnection(url); |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】线程__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__开始下载【开始位置 : " |
|
||||||
+ mConfigEntity.START_LOCATION |
|
||||||
+ ",结束位置:" |
|
||||||
+ mConfigEntity.END_LOCATION |
|
||||||
+ "】"); |
|
||||||
//在头里面请求下载开始位置和结束位置
|
|
||||||
conn.setRequestProperty("Range", |
|
||||||
"bytes=" + mConfigEntity.START_LOCATION + "-" + (mConfigEntity.END_LOCATION - 1)); |
|
||||||
} else { |
|
||||||
Log.w(TAG, "该下载不支持断点"); |
|
||||||
} |
|
||||||
conn = ConnectionHelp.setConnectParam(mConfigEntity.DOWNLOAD_TASK_ENTITY, conn); |
|
||||||
conn.setConnectTimeout(STATE.CONNECT_TIME_OUT); |
|
||||||
conn.setReadTimeout(STATE.READ_TIME_OUT); //设置读取流的等待时间,必须设置该参数
|
|
||||||
is = conn.getInputStream(); |
|
||||||
//创建可设置位置的文件
|
|
||||||
file = new BufferedRandomAccessFile(mConfigEntity.TEMP_FILE, "rwd", mBufSize); |
|
||||||
//设置每条线程写入文件的位置
|
|
||||||
file.seek(mConfigEntity.START_LOCATION); |
|
||||||
byte[] buffer = new byte[mBufSize]; |
|
||||||
int len; |
|
||||||
//当前子线程的下载位置
|
|
||||||
mChildCurrentLocation = mConfigEntity.START_LOCATION; |
|
||||||
while ((len = is.read(buffer)) != -1) { |
|
||||||
if (STATE.isCancel) break; |
|
||||||
if (STATE.isStop) break; |
|
||||||
if (mSleepTime > 0) Thread.sleep(mSleepTime); |
|
||||||
file.write(buffer, 0, len); |
|
||||||
progress(len); |
|
||||||
} |
|
||||||
if (STATE.isCancel) return; |
|
||||||
//停止状态不需要删除记录文件
|
|
||||||
if (STATE.isStop) return; |
|
||||||
//支持断点的处理
|
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
Log.i(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】线程__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__下载完毕"); |
|
||||||
writeConfig(true, 1); |
|
||||||
STATE.COMPLETE_THREAD_NUM++; |
|
||||||
if (STATE.isComplete()) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
if (configFile.exists()) { |
|
||||||
configFile.delete(); |
|
||||||
} |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onComplete(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.i(TAG, "下载任务完成"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onComplete(); |
|
||||||
} |
|
||||||
} catch (MalformedURLException e) { |
|
||||||
failDownload(mChildCurrentLocation, "下载链接异常", e); |
|
||||||
} catch (IOException e) { |
|
||||||
failDownload(mChildCurrentLocation, "下载失败【" + mConfigEntity.DOWNLOAD_URL + "】", e); |
|
||||||
} catch (Exception e) { |
|
||||||
failDownload(mChildCurrentLocation, "获取流失败", e); |
|
||||||
} finally { |
|
||||||
try { |
|
||||||
if (file != null) { |
|
||||||
file.close(); |
|
||||||
} |
|
||||||
if (is != null) { |
|
||||||
is.close(); |
|
||||||
} |
|
||||||
if (conn != null) { |
|
||||||
conn.disconnect(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止下载 |
|
||||||
*/ |
|
||||||
protected void stop() { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
try { |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
STATE.STOP_NUM++; |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】thread__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__停止, stop location ==> " |
|
||||||
+ mChildCurrentLocation); |
|
||||||
writeConfig(false, mChildCurrentLocation); |
|
||||||
if (STATE.isStop()) { |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已停止"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onStop(STATE.CURRENT_LOCATION); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已停止"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onStop(STATE.CURRENT_LOCATION); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 下载中 |
|
||||||
*/ |
|
||||||
private void progress(long len) { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
mChildCurrentLocation += len; |
|
||||||
STATE.CURRENT_LOCATION += len; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 取消下载 |
|
||||||
*/ |
|
||||||
protected void cancel() { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
STATE.CANCEL_NUM++; |
|
||||||
Log.d(TAG, "任务【" |
|
||||||
+ mConfigEntity.TEMP_FILE.getName() |
|
||||||
+ "】thread__" |
|
||||||
+ mConfigEntity.THREAD_ID |
|
||||||
+ "__取消下载"); |
|
||||||
if (STATE.isCancel()) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
if (configFile.exists()) { |
|
||||||
configFile.delete(); |
|
||||||
} |
|
||||||
if (mConfigEntity.TEMP_FILE.exists()) { |
|
||||||
mConfigEntity.TEMP_FILE.delete(); |
|
||||||
} |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已取消"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onCancel(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.d(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】已取消"); |
|
||||||
STATE.isDownloading = false; |
|
||||||
mListener.onCancel(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 下载失败 |
|
||||||
*/ |
|
||||||
private void failDownload(long currentLocation, String msg, Exception ex) { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
try { |
|
||||||
STATE.FAIL_NUM++; |
|
||||||
STATE.isDownloading = false; |
|
||||||
STATE.isStop = true; |
|
||||||
if (ex != null) { |
|
||||||
Log.e(TAG, msg + "\n" + CommonUtil.getPrintException(ex)); |
|
||||||
} |
|
||||||
if (mConfigEntity.IS_SUPPORT_BREAK_POINT) { |
|
||||||
writeConfig(false, currentLocation); |
|
||||||
if (STATE.isFail()) { |
|
||||||
Log.e(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】下载失败"); |
|
||||||
mListener.onFail(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
Log.e(TAG, "任务【" + mConfigEntity.TEMP_FILE.getName() + "】下载失败"); |
|
||||||
mListener.onFail(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 将记录写入到配置文件 |
|
||||||
*/ |
|
||||||
private void writeConfig(boolean isComplete, long record) throws IOException { |
|
||||||
synchronized (AriaManager.LOCK) { |
|
||||||
String key = null, value = null; |
|
||||||
if (0 < record && record < mConfigEntity.END_LOCATION) { |
|
||||||
key = mConfigEntity.TEMP_FILE.getName() + "_record_" + mConfigEntity.THREAD_ID; |
|
||||||
value = String.valueOf(record); |
|
||||||
} else if (record >= mConfigEntity.END_LOCATION || isComplete) { |
|
||||||
key = mConfigEntity.TEMP_FILE.getName() + "_state_" + mConfigEntity.THREAD_ID; |
|
||||||
value = "1"; |
|
||||||
} |
|
||||||
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) { |
|
||||||
File configFile = new File(mConfigFPath); |
|
||||||
Properties pro = CommonUtil.loadConfig(configFile); |
|
||||||
pro.setProperty(key, value); |
|
||||||
CommonUtil.saveConfig(configFile, pro); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,106 @@ |
|||||||
|
/* |
||||||
|
* 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.simple.download; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
import com.arialyy.annotations.Download; |
||||||
|
import com.arialyy.aria.core.Aria; |
||||||
|
import com.arialyy.aria.core.download.DownloadTask; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import com.arialyy.frame.util.show.L; |
||||||
|
import com.arialyy.frame.util.show.T; |
||||||
|
import com.arialyy.simple.R; |
||||||
|
import com.arialyy.simple.base.BaseActivity; |
||||||
|
import com.arialyy.simple.databinding.ActivityFtpDownloadBinding; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/25. |
||||||
|
* Ftp下载测试 |
||||||
|
*/ |
||||||
|
public class FtpDownloadActivity extends BaseActivity<ActivityFtpDownloadBinding> { |
||||||
|
|
||||||
|
@Override protected void init(Bundle savedInstanceState) { |
||||||
|
super.init(savedInstanceState); |
||||||
|
Aria.download(this).register(); |
||||||
|
} |
||||||
|
|
||||||
|
public void onClick(View view) { |
||||||
|
switch (view.getId()) { |
||||||
|
case R.id.start: |
||||||
|
Aria.download(this) |
||||||
|
//.load("172.18.104.129", "21", "cd.mp3")
|
||||||
|
.load("172.18.104.129", "21", "gg.png") |
||||||
|
.login("lao", "123456") |
||||||
|
.setDownloadPath("/mnt/sdcard/tt.png") |
||||||
|
.charSet("gbk") |
||||||
|
.start(); |
||||||
|
break; |
||||||
|
case R.id.stop: |
||||||
|
break; |
||||||
|
case R.id.cancel: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onPre() protected void onPre(DownloadTask task) { |
||||||
|
L.d(TAG, "ftp pre"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskPre() protected void onTaskPre(DownloadTask task) { |
||||||
|
L.d(TAG, "ftp task pre"); |
||||||
|
getBinding().setFileSize(task.getConvertFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStart() void taskStart(DownloadTask task) { |
||||||
|
L.d(TAG, "ftp task start"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskRunning() protected void running(DownloadTask task) { |
||||||
|
getBinding().setProgress(task.getPercent()); |
||||||
|
getBinding().setSpeed(task.getConvertSpeed()); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskResume() void taskResume(DownloadTask task) { |
||||||
|
L.d(TAG, "ftp task resume"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskStop() void taskStop(DownloadTask task) { |
||||||
|
L.d(TAG, "ftp task stop"); |
||||||
|
getBinding().setSpeed(""); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskCancel() void taskCancel(DownloadTask task) { |
||||||
|
getBinding().setSpeed(""); |
||||||
|
getBinding().setProgress(0); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskFail() void taskFail(DownloadTask task) { |
||||||
|
L.d(TAG, "ftp task fail"); |
||||||
|
} |
||||||
|
|
||||||
|
@Download.onTaskComplete() void taskComplete(DownloadTask task) { |
||||||
|
getBinding().setProgress(100); |
||||||
|
Log.d(TAG, "md5 ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); |
||||||
|
T.showShort(this, "FTP下载完成"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected int setLayoutId() { |
||||||
|
return R.layout.activity_ftp_download; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:bind="http://schemas.android.com/apk/res-auto" |
||||||
|
> |
||||||
|
<data> |
||||||
|
<variable |
||||||
|
name="fileSize" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="speed" |
||||||
|
type="String" |
||||||
|
/> |
||||||
|
<variable |
||||||
|
name="progress" |
||||||
|
type="int" |
||||||
|
/> |
||||||
|
</data> |
||||||
|
|
||||||
|
<LinearLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:orientation="vertical" |
||||||
|
> |
||||||
|
|
||||||
|
<include layout="@layout/layout_bar"/> |
||||||
|
|
||||||
|
<include |
||||||
|
layout="@layout/content_single" |
||||||
|
bind:fileSize="@{fileSize}" |
||||||
|
bind:progress="@{progress}" |
||||||
|
bind:speed="@{speed}" |
||||||
|
/> |
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
</layout> |
Loading…
Reference in new issue