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