parent
1f527572e7
commit
4f40341ee9
@ -0,0 +1,395 @@ |
|||||||
|
/* |
||||||
|
* 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.common; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.util.Log; |
||||||
|
import android.util.SparseArray; |
||||||
|
import com.arialyy.aria.core.AriaManager; |
||||||
|
import com.arialyy.aria.core.download.DownloadTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsNormalEntity; |
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import com.arialyy.aria.core.inf.IDownloadListener; |
||||||
|
import com.arialyy.aria.core.inf.IEventListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
||||||
|
import com.arialyy.aria.util.CommonUtil; |
||||||
|
import java.io.File; |
||||||
|
import java.util.Properties; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by AriaL on 2017/7/1. |
||||||
|
* 文件下载器 |
||||||
|
*/ |
||||||
|
public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_ENTITY extends AbsTaskEntity<ENTITY>> |
||||||
|
implements Runnable, IUtil { |
||||||
|
private final String TAG = "Downloader"; |
||||||
|
protected IEventListener mListener; |
||||||
|
protected TASK_ENTITY mTaskEntity; |
||||||
|
protected ENTITY mEntity; |
||||||
|
protected File mConfigFile;//信息配置文件
|
||||||
|
protected Context mContext; |
||||||
|
protected File mTempFile; //下载的文件
|
||||||
|
protected boolean isNewTask = true; |
||||||
|
protected StateConstance mConstance; |
||||||
|
private ExecutorService mFixedThreadPool; |
||||||
|
private int mThreadNum, mRealThreadNum; |
||||||
|
private SparseArray<AbsThreadTask> mTask = new SparseArray<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 小于1m的文件不启用多线程 |
||||||
|
*/ |
||||||
|
private static final long SUB_LEN = 1024 * 1024; |
||||||
|
private Timer mTimer; |
||||||
|
|
||||||
|
protected AbsFileer(IEventListener listener, TASK_ENTITY taskEntity) { |
||||||
|
mListener = listener; |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
mEntity = mTaskEntity.getEntity(); |
||||||
|
mContext = AriaManager.APP; |
||||||
|
mConstance = new StateConstance(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void setMaxSpeed(double maxSpeed) { |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
AbsThreadTask task = mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.setMaxSpeed(maxSpeed); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public StateConstance getConstance() { |
||||||
|
return mConstance; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
startFlow(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始下载流程 |
||||||
|
*/ |
||||||
|
private void startFlow() { |
||||||
|
checkTask(); |
||||||
|
if (mListener instanceof IDownloadListener) { |
||||||
|
((IDownloadListener) mListener).onPostPre(mEntity.getFileSize()); |
||||||
|
} |
||||||
|
mConstance.cleanState(); |
||||||
|
mConstance.isRunning = true; |
||||||
|
if (!mTaskEntity.isSupportBP) { |
||||||
|
mThreadNum = 1; |
||||||
|
mConstance.THREAD_NUM = mThreadNum; |
||||||
|
handleNoSupportBP(); |
||||||
|
} else { |
||||||
|
mThreadNum = isNewTask ? (getThreadNum()) : mRealThreadNum; |
||||||
|
mConstance.THREAD_NUM = mThreadNum; |
||||||
|
mFixedThreadPool = Executors.newFixedThreadPool(mThreadNum); |
||||||
|
handleBreakpoint(); |
||||||
|
} |
||||||
|
startTimer(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取线程数 |
||||||
|
*/ |
||||||
|
protected int getThreadNum() { |
||||||
|
return mEntity.getFileSize() <= SUB_LEN || mTaskEntity.requestType == AbsTaskEntity.FTP_DIR ? 1 |
||||||
|
: AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动进度获取定时器 |
||||||
|
*/ |
||||||
|
private void startTimer() { |
||||||
|
mTimer = new Timer(true); |
||||||
|
mTimer.schedule(new TimerTask() { |
||||||
|
@Override public void run() { |
||||||
|
if (mConstance.isComplete() || !mConstance.isRunning) { |
||||||
|
closeTimer(); |
||||||
|
} else if (mConstance.CURRENT_LOCATION >= 0) { |
||||||
|
mListener.onProgress(mConstance.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
} |
||||||
|
}, 0, 1000); |
||||||
|
} |
||||||
|
|
||||||
|
private void closeTimer() { |
||||||
|
if (mTimer != null) { |
||||||
|
mTimer.purge(); |
||||||
|
mTimer.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return mEntity.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前下载位置 |
||||||
|
*/ |
||||||
|
@Override public long getCurrentLocation() { |
||||||
|
return mConstance.CURRENT_LOCATION; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isRunning() { |
||||||
|
return mConstance.isRunning; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void cancel() { |
||||||
|
closeTimer(); |
||||||
|
mConstance.isCancel = true; |
||||||
|
mConstance.isRunning = false; |
||||||
|
if (mFixedThreadPool != null) { |
||||||
|
mFixedThreadPool.shutdown(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
AbsThreadTask task = mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
if (mTaskEntity instanceof DownloadTaskEntity) { |
||||||
|
CommonUtil.delDownloadTaskConfig(mTaskEntity.removeFile, (DownloadTaskEntity) mTaskEntity); |
||||||
|
} else if (mTaskEntity instanceof UploadTaskEntity) { |
||||||
|
CommonUtil.delUploadTaskConfig(mTaskEntity.removeFile, (UploadTaskEntity) mTaskEntity); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void stop() { |
||||||
|
closeTimer(); |
||||||
|
if (mConstance.isComplete()) return; |
||||||
|
mConstance.isStop = true; |
||||||
|
mConstance.isRunning = false; |
||||||
|
if (mFixedThreadPool != null) { |
||||||
|
mFixedThreadPool.shutdown(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
AbsThreadTask task = mTask.get(i); |
||||||
|
if (task != null) { |
||||||
|
task.stop(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 直接调用的时候会自动启动线程执行 |
||||||
|
*/ |
||||||
|
@Override public void start() { |
||||||
|
new Thread(this).start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void resume() { |
||||||
|
start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 返回该下载器的 |
||||||
|
*/ |
||||||
|
public IEventListener getListener() { |
||||||
|
return mListener; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查任务是否是新任务,新任务条件: |
||||||
|
* 1、文件不存在 |
||||||
|
* 2、下载记录文件不存在 |
||||||
|
* 3、下载记录文件缺失或不匹配 |
||||||
|
* 4、数据库记录不存在 |
||||||
|
* 5、不支持断点,则是新任务 |
||||||
|
*/ |
||||||
|
protected abstract void checkTask(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查记录文件,如果是新任务返回{@code true},否则返回{@code false} |
||||||
|
*/ |
||||||
|
protected boolean checkConfigFile() { |
||||||
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
||||||
|
if (pro.isEmpty()) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
Set<Object> keys = pro.keySet(); |
||||||
|
int num = 0; |
||||||
|
for (Object key : keys) { |
||||||
|
if (String.valueOf(key).contains("_record_")) { |
||||||
|
num++; |
||||||
|
} |
||||||
|
} |
||||||
|
if (num == 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
mRealThreadNum = num; |
||||||
|
for (int i = 0; i < mRealThreadNum; i++) { |
||||||
|
if (pro.getProperty(mTempFile.getName() + "_record_" + i) == null) { |
||||||
|
Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); |
||||||
|
if (state != null && Integer.parseInt(state + "") == 1) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 恢复记录地址 |
||||||
|
* |
||||||
|
* @return true 表示下载完成 |
||||||
|
*/ |
||||||
|
private boolean resumeRecordLocation(int i, long startL, long endL) { |
||||||
|
mConstance.CURRENT_LOCATION += endL - startL; |
||||||
|
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); |
||||||
|
mConstance.COMPLETE_THREAD_NUM++; |
||||||
|
mConstance.STOP_NUM++; |
||||||
|
mConstance.CANCEL_NUM++; |
||||||
|
if (mConstance.isComplete()) { |
||||||
|
if (mConfigFile.exists()) { |
||||||
|
mConfigFile.delete(); |
||||||
|
} |
||||||
|
mListener.onComplete(); |
||||||
|
mConstance.isRunning = false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动断点任务时,创建单线程任务 |
||||||
|
* |
||||||
|
* @param i 线程id |
||||||
|
* @param startL 该任务起始位置 |
||||||
|
* @param endL 该任务结束位置 |
||||||
|
* @param fileLength 该任务需要处理的文件长度 |
||||||
|
*/ |
||||||
|
private AbsThreadTask createSingThreadTask(int i, long startL, long endL, long fileLength) { |
||||||
|
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>(); |
||||||
|
config.FILE_SIZE = fileLength; |
||||||
|
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); |
||||||
|
config.TEMP_FILE = mTempFile; |
||||||
|
config.THREAD_ID = i; |
||||||
|
config.START_LOCATION = startL; |
||||||
|
config.END_LOCATION = endL; |
||||||
|
config.CONFIG_FILE_PATH = mConfigFile.getPath(); |
||||||
|
config.SUPPORT_BP = mTaskEntity.isSupportBP; |
||||||
|
config.TASK_ENTITY = mTaskEntity; |
||||||
|
return selectThreadTask(config); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动单线程下载任务 |
||||||
|
*/ |
||||||
|
private void startSingleTask(int[] recordL) { |
||||||
|
if (mConstance.CURRENT_LOCATION > 0) { |
||||||
|
mListener.onResume(mConstance.CURRENT_LOCATION); |
||||||
|
} else { |
||||||
|
mListener.onStart(mConstance.CURRENT_LOCATION); |
||||||
|
} |
||||||
|
mFixedThreadPool = Executors.newFixedThreadPool(recordL.length); |
||||||
|
for (int l : recordL) { |
||||||
|
if (l == -1) continue; |
||||||
|
Runnable task = mTask.get(l); |
||||||
|
if (task != null) { |
||||||
|
mFixedThreadPool.execute(task); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理断点 |
||||||
|
*/ |
||||||
|
private void handleBreakpoint() { |
||||||
|
long fileLength = mEntity.getFileSize(); |
||||||
|
Properties pro = CommonUtil.loadConfig(mConfigFile); |
||||||
|
int blockSize = (int) (fileLength / mThreadNum); |
||||||
|
int[] recordL = new int[mThreadNum]; |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
recordL[i] = -1; |
||||||
|
} |
||||||
|
int rl = 0; |
||||||
|
if (isNewTask) { |
||||||
|
handleNewTask(); |
||||||
|
} |
||||||
|
for (int i = 0; i < mThreadNum; i++) { |
||||||
|
long startL = i * blockSize, endL = (i + 1) * blockSize; |
||||||
|
Object state = pro.getProperty(mTempFile.getName() + "_state_" + i); |
||||||
|
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
|
||||||
|
if (resumeRecordLocation(i, startL, endL)) return; |
||||||
|
continue; |
||||||
|
} |
||||||
|
//分配下载位置
|
||||||
|
Object record = pro.getProperty(mTempFile.getName() + "_record_" + i); |
||||||
|
//如果有记录,则恢复下载
|
||||||
|
if (!isNewTask && record != null && Long.parseLong(record + "") >= 0) { |
||||||
|
Long r = Long.parseLong(record + ""); |
||||||
|
mConstance.CURRENT_LOCATION += r - startL; |
||||||
|
Log.d(TAG, "任务【" + mEntity.getFileName() + "】线程__" + i + "__恢复下载"); |
||||||
|
startL = r; |
||||||
|
recordL[rl] = i; |
||||||
|
rl++; |
||||||
|
} else { |
||||||
|
recordL[rl] = i; |
||||||
|
rl++; |
||||||
|
} |
||||||
|
if (i == (mThreadNum - 1)) { |
||||||
|
//最后一个线程的结束位置即为文件的总长度
|
||||||
|
endL = fileLength; |
||||||
|
} |
||||||
|
AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength); |
||||||
|
if (task == null) return; |
||||||
|
mTask.put(i, task); |
||||||
|
} |
||||||
|
startSingleTask(recordL); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract void handleNewTask(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理不支持断点的下载 |
||||||
|
*/ |
||||||
|
private void handleNoSupportBP() { |
||||||
|
SubThreadConfig<TASK_ENTITY> config = new SubThreadConfig<>(); |
||||||
|
config.FILE_SIZE = mEntity.getFileSize(); |
||||||
|
config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); |
||||||
|
config.TEMP_FILE = mTempFile; |
||||||
|
config.THREAD_ID = 0; |
||||||
|
config.START_LOCATION = 0; |
||||||
|
config.END_LOCATION = config.FILE_SIZE; |
||||||
|
config.CONFIG_FILE_PATH = mConfigFile.getPath(); |
||||||
|
config.SUPPORT_BP = mTaskEntity.isSupportBP; |
||||||
|
config.TASK_ENTITY = mTaskEntity; |
||||||
|
AbsThreadTask task = selectThreadTask(config); |
||||||
|
if (task == null) return; |
||||||
|
mTask.put(0, task); |
||||||
|
mFixedThreadPool.execute(task); |
||||||
|
mListener.onStart(0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 选择单任务线程的类型 |
||||||
|
*/ |
||||||
|
protected abstract AbsThreadTask selectThreadTask(SubThreadConfig<TASK_ENTITY> config); |
||||||
|
|
||||||
|
protected void failDownload(String errorMsg) { |
||||||
|
closeTimer(); |
||||||
|
Log.e(TAG, errorMsg); |
||||||
|
mConstance.isRunning = false; |
||||||
|
mListener.onFail(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.arialyy.aria.core.common; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.inf.AbsTaskEntity; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* 子线程下载信息类 |
||||||
|
*/ |
||||||
|
public class SubThreadConfig<TASK_ENTITY extends AbsTaskEntity> { |
||||||
|
//线程Id
|
||||||
|
public int THREAD_ID; |
||||||
|
//下载文件大小
|
||||||
|
public long FILE_SIZE; |
||||||
|
//子线程启动下载位置
|
||||||
|
public long START_LOCATION; |
||||||
|
//子线程结束下载位置
|
||||||
|
public long END_LOCATION; |
||||||
|
//下载文件或上传的文件路径
|
||||||
|
public File TEMP_FILE; |
||||||
|
//服务器地址
|
||||||
|
public String URL; |
||||||
|
public String CONFIG_FILE_PATH; |
||||||
|
public TASK_ENTITY TASK_ENTITY; |
||||||
|
public boolean SUPPORT_BP = true; |
||||||
|
} |
@ -1,25 +0,0 @@ |
|||||||
package com.arialyy.aria.core.download.downloader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.download.DownloadTaskEntity; |
|
||||||
import java.io.File; |
|
||||||
import org.apache.commons.net.ftp.FTPClient; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子线程下载信息类 |
|
||||||
*/ |
|
||||||
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; |
|
||||||
} |
|
@ -0,0 +1,186 @@ |
|||||||
|
/* |
||||||
|
* 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.upload.uploader; |
||||||
|
|
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.Log; |
||||||
|
import com.arialyy.aria.core.common.AbsThreadTask; |
||||||
|
import com.arialyy.aria.core.common.StateConstance; |
||||||
|
import com.arialyy.aria.core.common.SubThreadConfig; |
||||||
|
import com.arialyy.aria.core.inf.IEventListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
||||||
|
import com.arialyy.aria.util.BufferedRandomAccessFile; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
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/28. |
||||||
|
* FTP 单线程下载器 |
||||||
|
*/ |
||||||
|
class FtpThreadTask extends AbsThreadTask<UploadEntity, UploadTaskEntity> { |
||||||
|
private final String TAG = "FtpThreadTask"; |
||||||
|
private String remotePath, charSet; |
||||||
|
|
||||||
|
FtpThreadTask(StateConstance constance, IEventListener listener, |
||||||
|
SubThreadConfig<UploadTaskEntity> info) { |
||||||
|
super(constance, listener, info); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
FTPClient client = null; |
||||||
|
OutputStream os = null; |
||||||
|
BufferedRandomAccessFile file = null; |
||||||
|
try { |
||||||
|
Log.d(TAG, "任务【" |
||||||
|
+ mConfig.TEMP_FILE.getName() |
||||||
|
+ "】线程__" |
||||||
|
+ mConfig.THREAD_ID |
||||||
|
+ "__开始上传【开始位置 : " |
||||||
|
+ mConfig.START_LOCATION |
||||||
|
+ ",结束位置:" |
||||||
|
+ mConfig.END_LOCATION |
||||||
|
+ "】"); |
||||||
|
//当前子线程的下载位置
|
||||||
|
mChildCurrentLocation = mConfig.START_LOCATION; |
||||||
|
client = createClient(); |
||||||
|
if (client == null) return; |
||||||
|
client.makeDirectory(remotePath); |
||||||
|
client.changeWorkingDirectory(remotePath); |
||||||
|
client.setRestartOffset(mConfig.START_LOCATION); |
||||||
|
file = new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", mBufSize); |
||||||
|
file.seek(mConfig.START_LOCATION); |
||||||
|
if (!isRemoteComplete(client)) { |
||||||
|
os = client.storeFileStream(new String(remotePath.getBytes(charSet), SERVER_CHARSET)); |
||||||
|
//发送第二次指令时,还需要再做一次判断
|
||||||
|
int reply = client.getReplyCode(); |
||||||
|
if (!FTPReply.isPositivePreliminary(reply)) { |
||||||
|
client.disconnect(); |
||||||
|
fail(mChildCurrentLocation, "上传文件错误,错误码为:" + reply, null); |
||||||
|
return; |
||||||
|
} |
||||||
|
upload(file, os); |
||||||
|
} |
||||||
|
if (STATE.isCancel || STATE.isStop) return; |
||||||
|
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.isRunning = false; |
||||||
|
mListener.onComplete(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
fail(mChildCurrentLocation, "上传失败【" + mConfig.URL + "】", e); |
||||||
|
} catch (Exception e) { |
||||||
|
fail(mChildCurrentLocation, "获取流失败", e); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (file != null) { |
||||||
|
file.close(); |
||||||
|
} |
||||||
|
if (os != null) { |
||||||
|
os.close(); |
||||||
|
} |
||||||
|
if (client != null && client.isConnected()) { |
||||||
|
client.disconnect(); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 远程文件是否已经玩飞车 |
||||||
|
* |
||||||
|
* @return true 任务已经完成 |
||||||
|
*/ |
||||||
|
private boolean isRemoteComplete(FTPClient client) throws IOException { |
||||||
|
FTPFile[] files = client.listFiles(new String(remotePath.getBytes(charSet), SERVER_CHARSET)); |
||||||
|
return files.length != 0 && files[0].getSize() == mEntity.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 执行上传操作 |
||||||
|
*/ |
||||||
|
private void upload(BufferedRandomAccessFile file, OutputStream os) |
||||||
|
throws IOException, InterruptedException { |
||||||
|
int len; |
||||||
|
byte[] buffer = new byte[mBufSize]; |
||||||
|
while ((len = file.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); |
||||||
|
os.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
break; |
||||||
|
} else { |
||||||
|
os.write(buffer, 0, len); |
||||||
|
progress(len); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建FTP客户端 |
||||||
|
*/ |
||||||
|
private FTPClient createClient() throws IOException { |
||||||
|
String url = mEntity.getUrl(); |
||||||
|
String[] pp = url.split("/")[2].split(":"); |
||||||
|
String serverIp = pp[0]; |
||||||
|
int port = Integer.parseInt(pp[1]); |
||||||
|
remotePath = url.substring(url.indexOf(pp[1]) + pp[1].length(), url.length()) |
||||||
|
+ "/" |
||||||
|
+ mEntity.getFileName(); |
||||||
|
FTPClient client = new FTPClient(); |
||||||
|
client.connect(serverIp, port); |
||||||
|
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(); |
||||||
|
fail(STATE.CURRENT_LOCATION, "无法连接到ftp服务器,错误码为:" + reply, null); |
||||||
|
return null; |
||||||
|
} |
||||||
|
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.allocate(mBufSize); |
||||||
|
return client; |
||||||
|
} |
||||||
|
} |
@ -1,66 +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.upload.uploader; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2016/10/31. |
|
||||||
* 抽象的下载接口 |
|
||||||
*/ |
|
||||||
public interface IUploadUtil { |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取文件大小 |
|
||||||
*/ |
|
||||||
long getFileSize(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取当前上传位置 |
|
||||||
*/ |
|
||||||
long getCurrentLocation(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 是否正在下载 |
|
||||||
* |
|
||||||
* @return true, 正在下载 |
|
||||||
*/ |
|
||||||
boolean isUploading(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 取消下载 |
|
||||||
*/ |
|
||||||
void cancelUpload(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止下载 |
|
||||||
*/ |
|
||||||
void stopUpload(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 开始下载 |
|
||||||
*/ |
|
||||||
void startUpload(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 从上次断点恢复下载 |
|
||||||
*/ |
|
||||||
void resumeUpload(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置最大下载速度 |
|
||||||
*/ |
|
||||||
void setMaxSpeed(double maxSpeed); |
|
||||||
} |
|
@ -0,0 +1,82 @@ |
|||||||
|
/* |
||||||
|
* 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.upload.uploader; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.common.IUtil; |
||||||
|
import com.arialyy.aria.core.inf.IUploadListener; |
||||||
|
import com.arialyy.aria.core.upload.UploadEntity; |
||||||
|
import com.arialyy.aria.core.upload.UploadTaskEntity; |
||||||
|
import com.arialyy.aria.util.CheckUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by lyy on 2017/2/9. |
||||||
|
* 简单的http文件上传工具 |
||||||
|
*/ |
||||||
|
public class SimpleHttpUploadUtil implements IUtil, Runnable { |
||||||
|
private static final String TAG = "SimpleHttpUploadUtil"; |
||||||
|
|
||||||
|
private UploadEntity mUploadEntity; |
||||||
|
private UploadTaskEntity mTaskEntity; |
||||||
|
private IUploadListener mListener; |
||||||
|
private Uploader mUploader; |
||||||
|
|
||||||
|
public SimpleHttpUploadUtil(UploadTaskEntity taskEntity, IUploadListener listener) { |
||||||
|
mTaskEntity = taskEntity; |
||||||
|
CheckUtil.checkTaskEntity(taskEntity); |
||||||
|
mUploadEntity = taskEntity.getEntity(); |
||||||
|
if (listener == null) { |
||||||
|
throw new IllegalArgumentException("上传监听不能为空"); |
||||||
|
} |
||||||
|
mListener = listener; |
||||||
|
mUploader = new Uploader(mListener, taskEntity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void run() { |
||||||
|
mUploader.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getFileSize() { |
||||||
|
return mUploader.getFileSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public long getCurrentLocation() { |
||||||
|
return mUploader.getCurrentLocation(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public boolean isRunning() { |
||||||
|
return mUploader.isRunning(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void cancel() { |
||||||
|
mUploader.cancel(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void stop() { |
||||||
|
mUploader.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void start() { |
||||||
|
mUploader.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void resume() { |
||||||
|
mUploader.cancel(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void setMaxSpeed(double maxSpeed) { |
||||||
|
mUploader.setMaxSpeed(maxSpeed); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
/* |
||||||
|
* 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.upload; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
import com.arialyy.annotations.Upload; |
||||||
|
import com.arialyy.aria.core.Aria; |
||||||
|
import com.arialyy.aria.core.upload.UploadTask; |
||||||
|
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.ActivityFtpUploadBinding; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Aria.Lao on 2017/7/28. |
||||||
|
* Ftp 文件上传demo |
||||||
|
*/ |
||||||
|
public class FtpUploadActivity extends BaseActivity<ActivityFtpUploadBinding> { |
||||||
|
private final String FILE_PATH = "/mnt/sdcard/王者军团.apk"; |
||||||
|
private final String URL = "ftp://172.18.104.129:21/upload/"; |
||||||
|
|
||||||
|
@Override protected void init(Bundle savedInstanceState) { |
||||||
|
super.init(savedInstanceState); |
||||||
|
Aria.upload(this).register(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override protected int setLayoutId() { |
||||||
|
return R.layout.activity_ftp_upload; |
||||||
|
} |
||||||
|
|
||||||
|
public void onClick(View view) { |
||||||
|
switch (view.getId()) { |
||||||
|
case R.id.start: |
||||||
|
Aria.upload(this).loadFtp(FILE_PATH).setUploadUrl(URL).login("lao", "123456").start(); |
||||||
|
break; |
||||||
|
case R.id.stop: |
||||||
|
Aria.upload(this).loadFtp(FILE_PATH).stop(); |
||||||
|
break; |
||||||
|
case R.id.cancel: |
||||||
|
Aria.upload(this).load(FILE_PATH).cancel(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onPre public void onPre(UploadTask task) { |
||||||
|
getBinding().setFileSize(task.getConvertFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskPre public void taskPre(UploadTask task) { |
||||||
|
L.d(TAG, "fileSize = " + task.getConvertFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskStart public void taskStart(UploadTask task) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskStop public void taskStop(UploadTask task) { |
||||||
|
getBinding().setSpeed(""); |
||||||
|
Log.d(TAG, "停止上传"); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskCancel public void taskCancel(UploadTask task) { |
||||||
|
getBinding().setSpeed(""); |
||||||
|
Log.d(TAG, "取消上传"); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskRunning public void taskRunning(UploadTask task) { |
||||||
|
getBinding().setProgress(task.getPercent()); |
||||||
|
getBinding().setSpeed(task.getConvertSpeed()); |
||||||
|
} |
||||||
|
|
||||||
|
@Upload.onTaskComplete public void taskComplete(UploadTask task) { |
||||||
|
getBinding().setProgress(100); |
||||||
|
getBinding().setSpeed(""); |
||||||
|
T.showShort(this, "上传完成"); |
||||||
|
} |
||||||
|
} |
@ -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