sftp上传实现
This commit is contained in:
@@ -141,57 +141,6 @@ public class FtpOption extends BaseOption {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置私钥证书路径
|
||||
*
|
||||
* @param prvKey 证书路径
|
||||
*/
|
||||
public FtpOption setPrvKey(String prvKey) {
|
||||
if (TextUtils.isEmpty(prvKey)) {
|
||||
ALog.e(TAG, "设置私钥证书失败,证书内容为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.prvKey = prvKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置私钥密码
|
||||
*
|
||||
* @param prvKeyPass 私钥密码
|
||||
*/
|
||||
public FtpOption setPrvKeyPass(String prvKeyPass) {
|
||||
if (TextUtils.isEmpty(prvKeyPass)) {
|
||||
ALog.e(TAG, "设置证书密码失败,证书密码为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.prvPass = prvKeyPass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置公钥证书
|
||||
*
|
||||
* @param pubKey 公钥证书内容
|
||||
*/
|
||||
public FtpOption setPubKey(String pubKey) {
|
||||
if (TextUtils.isEmpty(pubKey)) {
|
||||
ALog.e(TAG, "设置公钥失败,证书内容为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.pubKey = pubKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FtpOption setKnowHostPath(String knowHostPath){
|
||||
if (TextUtils.isEmpty(knowHostPath)){
|
||||
ALog.e(TAG, "knowhost 文件路径为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.knowHost = knowHostPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置安全模式,默认true
|
||||
*
|
||||
|
||||
135
Aria/src/main/java/com/arialyy/aria/core/common/SFtpOption.java
Normal file
135
Aria/src/main/java/com/arialyy/aria/core/common/SFtpOption.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.text.TextUtils;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import com.arialyy.aria.core.IdEntity;
|
||||
import com.arialyy.aria.core.ProtocolType;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.ComponentUtil;
|
||||
|
||||
/**
|
||||
* Created by laoyuyu on 2018/3/9.
|
||||
*/
|
||||
public class SFtpOption extends BaseOption {
|
||||
|
||||
private String charSet, userName, password;
|
||||
private boolean isNeedLogin = false;
|
||||
private FtpUrlEntity urlEntity;
|
||||
private String protocol;
|
||||
private IdEntity idEntity = new IdEntity();
|
||||
|
||||
public SFtpOption() {
|
||||
super();
|
||||
ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_M3U8);
|
||||
}
|
||||
|
||||
public SFtpOption charSet(String charSet) {
|
||||
if (TextUtils.isEmpty(charSet)) {
|
||||
throw new NullPointerException("字符编码为空");
|
||||
}
|
||||
this.charSet = charSet;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SFtpOption login(String userName, String password) {
|
||||
if (TextUtils.isEmpty(userName)) {
|
||||
ALog.e(TAG, "用户名不能为null");
|
||||
return this;
|
||||
} else if (TextUtils.isEmpty(password)) {
|
||||
ALog.e(TAG, "密码不能为null");
|
||||
return this;
|
||||
}
|
||||
this.userName = userName;
|
||||
this.password = password;
|
||||
isNeedLogin = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置协议类型
|
||||
*
|
||||
* @param protocol {@link ProtocolType}
|
||||
*/
|
||||
public SFtpOption setProtocol(String protocol) {
|
||||
if (TextUtils.isEmpty(protocol)) {
|
||||
ALog.e(TAG, "设置协议失败,协议信息为空");
|
||||
return this;
|
||||
}
|
||||
this.protocol = protocol;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置私钥证书路径
|
||||
*
|
||||
* @param prvKey 证书路径
|
||||
*/
|
||||
public SFtpOption setPrvKey(String prvKey) {
|
||||
if (TextUtils.isEmpty(prvKey)) {
|
||||
ALog.e(TAG, "设置私钥证书失败,证书内容为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.prvKey = prvKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置私钥密码
|
||||
*
|
||||
* @param prvKeyPass 私钥密码
|
||||
*/
|
||||
public SFtpOption setPrvKeyPass(String prvKeyPass) {
|
||||
if (TextUtils.isEmpty(prvKeyPass)) {
|
||||
ALog.e(TAG, "设置证书密码失败,证书密码为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.prvPass = prvKeyPass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置公钥证书
|
||||
*
|
||||
* @param pubKey 公钥证书内容
|
||||
*/
|
||||
public SFtpOption setPubKey(String pubKey) {
|
||||
if (TextUtils.isEmpty(pubKey)) {
|
||||
ALog.e(TAG, "设置公钥失败,证书内容为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.pubKey = pubKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SFtpOption setKnowHostPath(String knowHostPath) {
|
||||
if (TextUtils.isEmpty(knowHostPath)) {
|
||||
ALog.e(TAG, "knowhost 文件路径为空");
|
||||
return this;
|
||||
}
|
||||
idEntity.knowHost = knowHostPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setUrlEntity(FtpUrlEntity urlEntity) {
|
||||
this.urlEntity = urlEntity;
|
||||
urlEntity.needLogin = isNeedLogin;
|
||||
urlEntity.user = userName;
|
||||
urlEntity.password = password;
|
||||
urlEntity.idEntity = idEntity;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package com.arialyy.aria.core.download.target;
|
||||
|
||||
import com.arialyy.aria.core.common.AbsBuilderTarget;
|
||||
import com.arialyy.aria.core.common.FtpOption;
|
||||
import com.arialyy.aria.core.common.SFtpOption;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
@@ -50,7 +51,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget<FtpBuilderTarget> {
|
||||
/**
|
||||
* 设置登陆、字符串编码、sftp等参数
|
||||
*/
|
||||
public FtpBuilderTarget sftpOption(FtpOption option) {
|
||||
public FtpBuilderTarget sftpOption(SFtpOption option) {
|
||||
if (option == null) {
|
||||
throw new NullPointerException("ftp 任务配置为空");
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.arialyy.aria.core.download.target;
|
||||
|
||||
import com.arialyy.aria.core.common.AbsNormalTarget;
|
||||
import com.arialyy.aria.core.common.FtpOption;
|
||||
import com.arialyy.aria.core.common.SFtpOption;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
@@ -49,7 +50,7 @@ public class FtpNormalTarget extends AbsNormalTarget<FtpNormalTarget> {
|
||||
/**
|
||||
* 设置登陆、字符串编码、sftp等参数
|
||||
*/
|
||||
public FtpNormalTarget sftpOption(FtpOption option) {
|
||||
public FtpNormalTarget sftpOption(SFtpOption option) {
|
||||
if (option == null) {
|
||||
throw new NullPointerException("ftp 任务配置为空");
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.arialyy.aria.core.upload.target;
|
||||
|
||||
import com.arialyy.aria.core.common.AbsBuilderTarget;
|
||||
import com.arialyy.aria.core.common.FtpOption;
|
||||
import com.arialyy.aria.core.common.SFtpOption;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
@@ -70,4 +71,18 @@ public class FtpBuilderTarget extends AbsBuilderTarget<FtpBuilderTarget> {
|
||||
getTaskWrapper().getOptionParams().setParams(option);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置登陆、字符串编码、sftp等参数
|
||||
*/
|
||||
public FtpBuilderTarget sftpOption(SFtpOption option) {
|
||||
if (option == null) {
|
||||
throw new NullPointerException("ftp 任务配置为空");
|
||||
}
|
||||
option.setUrlEntity(CommonUtil.getFtpUrlInfo(url));
|
||||
getTaskWrapper().getOptionParams().setParams(option);
|
||||
((UploadEntity)getEntity()).setTaskType(ITaskWrapper.U_SFTP);
|
||||
getTaskWrapper().setRequestType(ITaskWrapper.U_SFTP);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.arialyy.aria.core.upload.target;
|
||||
|
||||
import com.arialyy.aria.core.common.AbsNormalTarget;
|
||||
import com.arialyy.aria.core.common.FtpOption;
|
||||
import com.arialyy.aria.core.common.SFtpOption;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
@@ -46,6 +47,20 @@ public class FtpNormalTarget extends AbsNormalTarget<FtpNormalTarget> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置登陆、字符串编码、sftp等参数
|
||||
*/
|
||||
public FtpNormalTarget sftpOption(SFtpOption option) {
|
||||
if (option == null) {
|
||||
throw new NullPointerException("ftp 任务配置为空");
|
||||
}
|
||||
option.setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl()));
|
||||
getTaskWrapper().getOptionParams().setParams(option);
|
||||
(getEntity()).setTaskType(ITaskWrapper.U_SFTP);
|
||||
getTaskWrapper().setRequestType(ITaskWrapper.U_SFTP);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public UploadEntity getEntity() {
|
||||
return (UploadEntity) super.getEntity();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
## 开发日志
|
||||
+ v_3.8.4
|
||||
+ v_3.8.5 (2020/1/18)
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/599
|
||||
- 增加密钥url转换器的参数 https://github.com/AriaLyy/Aria/issues/603
|
||||
- 增加sftp,文件上传、下载功能,[sftp下载](https://aria.laoyuyu.me/aria_doc/download/sftp_normal.html),[sftp上传](https://aria.laoyuyu.me/aria_doc/upload/sftp_normal.html)
|
||||
- 使用零拷贝技术,优化了合并分块的效率
|
||||
+ v_3.8.3 (2020/1/9)
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/573
|
||||
- android P适配 https://github.com/AriaLyy/Aria/issues/581
|
||||
|
||||
@@ -1,95 +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.ftp.download;
|
||||
//
|
||||
//import com.arialyy.aria.core.TaskRecord;
|
||||
//import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
//import com.arialyy.aria.core.listener.IEventListener;
|
||||
//import com.arialyy.aria.core.loader.NormalTTBuilder;
|
||||
//import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
//import com.arialyy.aria.core.loader.IThreadTaskBuilder;
|
||||
//import com.arialyy.aria.core.loader.NormalLoader;
|
||||
//import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
//import com.arialyy.aria.util.ALog;
|
||||
//import com.arialyy.aria.util.FileUtil;
|
||||
//import java.io.File;
|
||||
//import java.lang.reflect.InvocationHandler;
|
||||
//import java.lang.reflect.Method;
|
||||
//import java.lang.reflect.Proxy;
|
||||
//
|
||||
//final class FtpDLoader extends NormalLoader {
|
||||
// FtpDLoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
// super(wrapper, listener);
|
||||
// }
|
||||
//
|
||||
// @Override public void addComponent(IThreadTaskBuilder builder) {
|
||||
// mTTBuilder = (IThreadTaskBuilder) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
// NormalTTBuilder.class.getInterfaces(), new InvocationHandler() {
|
||||
// NormalTTBuilder target = new NormalTTBuilder(mTaskWrapper);
|
||||
// @Override public Object invoke(Object proxy, Method method, Object[] args)
|
||||
// throws Exception {
|
||||
// if (method.getDeclaringClass() == Object.class) {
|
||||
// return method.invoke(this, args);
|
||||
// }
|
||||
//
|
||||
// //if (method.isDefault()) {
|
||||
// // Constructor<MethodHandles.Lookup>
|
||||
// // constructor =
|
||||
// // MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
|
||||
// // constructor.setAccessible(true);
|
||||
// // return constructor.newInstance(AbsNormalTTBuilder.class, -1 /* trusted */)
|
||||
// // .unreflectSpecial(method, AbsNormalTTBuilder.class)
|
||||
// // .bindTo(proxy)
|
||||
// // .invokeWithArguments(args);
|
||||
// //}
|
||||
//
|
||||
// String methodName = method.getName();
|
||||
// switch (methodName) {
|
||||
//
|
||||
// case "handleNewTask":
|
||||
// return handleNewTask((TaskRecord) args[0], (int) args[1]);
|
||||
// case "getAdapter":
|
||||
// return new FtpDThreadTaskAdapter((SubThreadConfig) args[0]);
|
||||
// }
|
||||
//
|
||||
// return method.invoke(target, args);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 处理新任务
|
||||
// */
|
||||
// private boolean handleNewTask(TaskRecord record, int totalThreadNum) {
|
||||
// File temp = new File(getEntity().getFilePath());
|
||||
// if (!record.isBlock) {
|
||||
// if (temp.exists()) {
|
||||
// FileUtil.deleteFile(temp);
|
||||
// }
|
||||
// //CommonUtil.createFile(mTempFile.getPath());
|
||||
// } else {
|
||||
// for (int i = 0; i < totalThreadNum; i++) {
|
||||
// File blockFile =
|
||||
// new File(String.format(IRecordHandler.SUB_PATH, temp.getPath(), i));
|
||||
// if (blockFile.exists()) {
|
||||
// ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i));
|
||||
// FileUtil.deleteFile(blockFile);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
@@ -30,9 +30,9 @@ import java.util.ArrayList;
|
||||
* @Author lyy
|
||||
* @Date 2019-09-19
|
||||
*/
|
||||
public final class FtpDRecordHandler extends RecordHandler {
|
||||
final class FtpDRecordHandler extends RecordHandler {
|
||||
|
||||
public FtpDRecordHandler(DTaskWrapper wrapper) {
|
||||
FtpDRecordHandler(DTaskWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public final class FtpDRecordHandler extends RecordHandler {
|
||||
record.threadRecords = new ArrayList<>();
|
||||
record.threadNum = threadNum;
|
||||
record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock();
|
||||
record.taskType = getWrapper().getRequestType();
|
||||
record.taskType = ITaskWrapper.D_FTP;
|
||||
record.isGroupRecord = false;
|
||||
|
||||
return record;
|
||||
|
||||
@@ -25,13 +25,13 @@ import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.loader.NormalLoader;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
|
||||
final class FtpULoader extends NormalLoader {
|
||||
final class FtpULoader extends NormalLoader<UTaskWrapper> {
|
||||
private FTPFile ftpFile;
|
||||
|
||||
FtpULoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
FtpULoader(UTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ final class FtpULoader extends NormalLoader {
|
||||
getListener().onStart(mStateManager.getCurrentProgress());
|
||||
}
|
||||
|
||||
startTimer();
|
||||
|
||||
// 启动线程任务
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask);
|
||||
|
||||
@@ -41,7 +41,8 @@ public final class FtpULoaderUtil extends AbsNormalLoaderUtil {
|
||||
}
|
||||
|
||||
@Override public AbsNormalLoader getLoader() {
|
||||
return mLoader == null ? new FtpULoader(getTaskWrapper(), getListener()) : mLoader;
|
||||
return mLoader == null ? new FtpULoader((UTaskWrapper) getTaskWrapper(), getListener())
|
||||
: mLoader;
|
||||
}
|
||||
|
||||
@Override public LoaderStructure BuildLoaderStructure() {
|
||||
|
||||
@@ -101,7 +101,7 @@ final class FtpURecordHandler extends RecordHandler {
|
||||
record.threadRecords = new ArrayList<>();
|
||||
record.threadNum = threadNum;
|
||||
record.isBlock = false;
|
||||
record.taskType = getWrapper().getRequestType();
|
||||
record.taskType = ITaskWrapper.U_FTP;
|
||||
record.isGroupRecord = getEntity().isGroupChild();
|
||||
|
||||
return record;
|
||||
|
||||
@@ -60,6 +60,10 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
|
||||
HttpURLConnection conn = null;
|
||||
BufferedInputStream is = null;
|
||||
BufferedRandomAccessFile file = null;
|
||||
if (getThreadRecord().threadId == 1){
|
||||
fail(null, false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
URL url = ConnectionHelp.handleUrl(getThreadConfig().url, mTaskOption);
|
||||
conn = ConnectionHelp.handleConnection(url, mTaskOption);
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.arialyy.aria.exception.AriaIOException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.util.List;
|
||||
|
||||
final class HttpULoader extends AbsNormalLoader {
|
||||
final class HttpULoader extends AbsNormalLoader<UTaskWrapper> {
|
||||
HttpULoader(UTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public abstract class BaseM3U8Loader extends AbsNormalLoader {
|
||||
public abstract class BaseM3U8Loader extends AbsNormalLoader<DTaskWrapper> {
|
||||
protected M3U8TaskOption mM3U8Option;
|
||||
|
||||
public BaseM3U8Loader(DTaskWrapper wrapper, IEventListener listener) {
|
||||
@@ -138,6 +138,6 @@ public abstract class BaseM3U8Loader extends AbsNormalLoader {
|
||||
}
|
||||
|
||||
protected DownloadEntity getEntity() {
|
||||
return (DownloadEntity) mTaskWrapper.getEntity();
|
||||
return mTaskWrapper.getEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ final public class M3U8InfoTask implements IInfoTask {
|
||||
IKeyUrlConverter keyUrlConverter = mM3U8Option.getKeyUrlConverter();
|
||||
String keyUrl = info.keyUrl;
|
||||
if (keyUrlConverter != null) {
|
||||
keyUrl = keyUrlConverter.convert(keyUrl);
|
||||
keyUrl = keyUrlConverter.convert(mEntity.getUrl(), keyUrl);
|
||||
}
|
||||
if (TextUtils.isEmpty(keyUrl)) {
|
||||
ALog.e(TAG, "m3u8密钥key url 为空");
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.arialyy.aria.http.ConnectionHelp;
|
||||
import com.arialyy.aria.http.HttpTaskOption;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CheckUtil;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -47,7 +48,7 @@ import java.util.Set;
|
||||
* Created by lyy on 2017/1/18. 下载线程
|
||||
*/
|
||||
public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
private final String TAG = "M3U8ThreadTask";
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
private HttpTaskOption mHttpTaskOption;
|
||||
private BufferedInputStream is = null;
|
||||
|
||||
@@ -167,7 +168,9 @@ public final class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
conn.disconnect(); // 关闭上一个连接
|
||||
URL url = ConnectionHelp.handleUrl(newUrl, mHttpTaskOption);
|
||||
conn = ConnectionHelp.handleConnection(url, mHttpTaskOption);
|
||||
conn.setRequestProperty("Cookie", cookies);
|
||||
if (!TextUtils.isEmpty(cookies)){
|
||||
conn.setRequestProperty("Cookie", cookies);
|
||||
}
|
||||
if (mHttpTaskOption.isChunked()) {
|
||||
conn.setDoInput(true);
|
||||
conn.setChunkedStreamingMode(0);
|
||||
|
||||
@@ -39,10 +39,10 @@ import java.util.concurrent.TimeUnit;
|
||||
* 3、创建文件信息获取器,获取文件信息,根据文件信息执行任务
|
||||
* 4、创建线程任务执行下载、上传操作
|
||||
*/
|
||||
public abstract class AbsNormalLoader implements ILoaderVisitor, ILoader {
|
||||
public abstract class AbsNormalLoader<T extends AbsTaskWrapper> implements ILoaderVisitor, ILoader {
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
private IEventListener mListener;
|
||||
protected AbsTaskWrapper mTaskWrapper;
|
||||
protected T mTaskWrapper;
|
||||
protected File mTempFile;
|
||||
|
||||
private List<IThreadTask> mTask = new ArrayList<>();
|
||||
@@ -61,7 +61,7 @@ public abstract class AbsNormalLoader implements ILoaderVisitor, ILoader {
|
||||
protected IInfoTask mInfoTask;
|
||||
protected IThreadTaskBuilder mTTBuilder;
|
||||
|
||||
protected AbsNormalLoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
protected AbsNormalLoader(T wrapper, IEventListener listener) {
|
||||
mListener = listener;
|
||||
mTaskWrapper = wrapper;
|
||||
}
|
||||
@@ -123,7 +123,7 @@ public abstract class AbsNormalLoader implements ILoaderVisitor, ILoader {
|
||||
}
|
||||
Looper.prepare();
|
||||
Looper looper = Looper.myLooper();
|
||||
if (looper == Looper.getMainLooper()){
|
||||
if (looper == Looper.getMainLooper()) {
|
||||
throw new IllegalThreadStateException("不能在主线程程序中调用Loader");
|
||||
}
|
||||
isRuning = true;
|
||||
|
||||
@@ -34,12 +34,12 @@ import java.io.File;
|
||||
/**
|
||||
* 单文件
|
||||
*/
|
||||
public class NormalLoader extends AbsNormalLoader {
|
||||
public class NormalLoader<T extends AbsTaskWrapper> extends AbsNormalLoader<T> {
|
||||
private int startThreadNum; //启动的线程数
|
||||
protected boolean isComplete = false;
|
||||
private Looper looper;
|
||||
|
||||
public NormalLoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
public NormalLoader(T wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
mTempFile = new File(getEntity().getFilePath());
|
||||
EventMsgUtil.getDefault().register(this);
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.os.Message;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
@@ -100,6 +99,11 @@ public class NormalThreadStateManager implements IThreadStateManager {
|
||||
mCompleteNum++;
|
||||
if (isComplete()) {
|
||||
ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum);
|
||||
//if (mTaskRecord.taskType == ITaskWrapper.D_SFTP) {
|
||||
// mergerSFtp();
|
||||
// mListener.onComplete();
|
||||
//} else
|
||||
|
||||
if (mTaskRecord.isBlock) {
|
||||
if (mergeFile()) {
|
||||
mListener.onComplete();
|
||||
@@ -215,6 +219,26 @@ public class NormalThreadStateManager implements IThreadStateManager {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并sftp的分块
|
||||
*/
|
||||
private boolean mergerSFtp() {
|
||||
if (mTaskRecord.threadNum == 1) {
|
||||
File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0));
|
||||
return partFile.renameTo(new File(mTaskRecord.filePath));
|
||||
}
|
||||
|
||||
List<String> partPath = new ArrayList<>();
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
FileUtil.mergeSFtpFile(mTaskRecord.filePath, partPath, mTaskRecord.fileLength);
|
||||
for (String pp : partPath) {
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并文件
|
||||
*
|
||||
@@ -230,15 +254,10 @@ public class NormalThreadStateManager implements IThreadStateManager {
|
||||
for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) {
|
||||
partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i));
|
||||
}
|
||||
boolean isSuccess = mTaskRecord.taskType == ITaskWrapper.D_SFTP ?
|
||||
FileUtil.mergeSFtpFile(mTaskRecord.filePath, partPath, mTaskRecord.fileLength)
|
||||
: FileUtil.mergeFile(mTaskRecord.filePath, partPath);
|
||||
boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath);
|
||||
if (isSuccess) {
|
||||
for (String pp : partPath) {
|
||||
File f = new File(pp);
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
FileUtil.deleteFile(pp);
|
||||
}
|
||||
File targetFile = new File(mTaskRecord.filePath);
|
||||
if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) {
|
||||
|
||||
@@ -25,8 +25,9 @@ public interface IKeyUrlConverter extends IEventHandler {
|
||||
/**
|
||||
* 将被加密的密钥下载地址转换为可使用的http下载地址
|
||||
*
|
||||
* @param m3u8Url m3u8文件的下载地址
|
||||
* @param keyUrl 加密的url地址
|
||||
* @return 可正常访问的http地址
|
||||
*/
|
||||
String convert(String keyUrl);
|
||||
String convert(String m3u8Url, String keyUrl);
|
||||
}
|
||||
|
||||
@@ -367,6 +367,11 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
b.putLong(IThreadStateManager.DATA_ADD_LEN, mRangeProgress - mLastRangeProgress);
|
||||
msg.what = IThreadStateManager.STATE_RUNNING;
|
||||
msg.obj = mRangeProgress;
|
||||
|
||||
Thread loopThread = mStateHandler.getLooper().getThread();
|
||||
if (!loopThread.isAlive() || loopThread.isInterrupted()) {
|
||||
return;
|
||||
}
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
@@ -451,6 +456,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|
||||
handleBlockRecord();
|
||||
ThreadTaskManager.getInstance().retryThread(this);
|
||||
} else {
|
||||
ALog.e(TAG, String.format("任务【%s】执行失败", getFileName()));
|
||||
sendFailMsg(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,11 @@ public interface ITaskWrapper {
|
||||
*/
|
||||
int D_SFTP = 12;
|
||||
|
||||
/**
|
||||
* SFTP 上传
|
||||
*/
|
||||
int U_SFTP = 13;
|
||||
|
||||
/**
|
||||
* 获取任务类型
|
||||
*
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -136,6 +135,18 @@ public class CommonUtil {
|
||||
return new String(str.getBytes(charSet), SERVER_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为Ftp服务器默认的ISO-8859-1编码
|
||||
*
|
||||
* @param charSet 字符串编码
|
||||
* @param str 需要转换的字符串
|
||||
* @return 转换后的字符串
|
||||
*/
|
||||
public static String convertSFtpChar(String charSet, String str)
|
||||
throws UnsupportedEncodingException {
|
||||
return new String(str.getBytes(), charSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某包下所有类
|
||||
*
|
||||
@@ -481,7 +492,6 @@ public class CommonUtil {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验文件MD5码
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,7 @@ public class ComponentUtil {
|
||||
public static final int COMPONENT_TYPE_HTTP = 1;
|
||||
public static final int COMPONENT_TYPE_FTP = 2;
|
||||
public static final int COMPONENT_TYPE_M3U8 = 3;
|
||||
public static final int COMPONENT_TYPE_SFTP = 4;
|
||||
|
||||
private String TAG = CommonUtil.getClassName(getClass());
|
||||
private static volatile ComponentUtil INSTANCE = null;
|
||||
@@ -82,6 +83,10 @@ public class ComponentUtil {
|
||||
className = "com.arialyy.aria.http.HttpTaskOption";
|
||||
errorStr = "http插件不存在,请添加http插件";
|
||||
break;
|
||||
case COMPONENT_TYPE_SFTP:
|
||||
className = "com.arialyy.aria.sftp.SFtpTaskOption";
|
||||
errorStr = "sftp插件不存在,请添加sftp插件";
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -130,6 +135,9 @@ public class ComponentUtil {
|
||||
case ITaskWrapper.D_SFTP:
|
||||
className = "com.arialyy.aria.sftp.download.SFtpDLoaderUtil";
|
||||
break;
|
||||
case ITaskWrapper.U_SFTP:
|
||||
className = "com.arialyy.aria.sftp.upload.SFtpULoaderUtil";
|
||||
break;
|
||||
}
|
||||
if (className == null) {
|
||||
ALog.e(TAG, "不识别的类名:" + className);
|
||||
@@ -178,6 +186,7 @@ public class ComponentUtil {
|
||||
break;
|
||||
case ITaskWrapper.U_FTP:
|
||||
case ITaskWrapper.U_HTTP:
|
||||
case ITaskWrapper.U_SFTP:
|
||||
className = "com.arialyy.aria.core.listener.BaseUListener";
|
||||
break;
|
||||
case ITaskWrapper.DG_HTTP:
|
||||
|
||||
@@ -37,17 +37,13 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -342,21 +338,12 @@ public class FileUtil {
|
||||
|
||||
return false;
|
||||
}
|
||||
streams.add(new FileInputStream(subPath));
|
||||
FileInputStream fis = new FileInputStream(subPath);
|
||||
FileChannel fic = fis.getChannel();
|
||||
foc.transferFrom(fic, fileLen, f.length());
|
||||
fileLen += f.length();
|
||||
fis.close();
|
||||
}
|
||||
Enumeration<FileInputStream> en = Collections.enumeration(streams);
|
||||
SequenceInputStream sis = new SequenceInputStream(en);
|
||||
ReadableByteChannel fic = Channels.newChannel(sis);
|
||||
//ByteBuffer bf = ByteBuffer.allocate(8196);
|
||||
//while (fic.read(bf) != -1) {
|
||||
// bf.flip();
|
||||
// foc.write(bf);
|
||||
// bf.compact();
|
||||
//}
|
||||
foc.transferFrom(fic, 0, fileLen);
|
||||
fic.close();
|
||||
sis.close();
|
||||
ALog.d(TAG, String.format("合并文件耗时:%sms", (System.currentTimeMillis() - startTime)));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
@@ -405,7 +392,6 @@ public class FileUtil {
|
||||
int i = 0;
|
||||
int threadNum = subPaths.size();
|
||||
long tempLen = targetFileSize / threadNum;
|
||||
ALog.d(TAG, "fileSize = " + targetFileSize);
|
||||
for (String subPath : subPaths) {
|
||||
File f = new File(subPath);
|
||||
if (!f.exists()) {
|
||||
@@ -421,14 +407,13 @@ public class FileUtil {
|
||||
long blockLen = i == (threadNum - 1) ? targetFileSize - tempLen * i : tempLen;
|
||||
FileInputStream fis = new FileInputStream(subPath);
|
||||
FileChannel fic = fis.getChannel();
|
||||
ALog.d(TAG, "blcokLen = " + blockLen);
|
||||
long rLen = foc.transferFrom(fic, 0, blockLen);
|
||||
ALog.d(TAG, "writeLen = " + rLen);
|
||||
foc.transferFrom(fic, blockLen * i, blockLen);
|
||||
fis.close();
|
||||
i ++;
|
||||
i++;
|
||||
}
|
||||
|
||||
ALog.d(TAG, String.format("合并文件耗时:%sms", (System.currentTimeMillis() - startTime)));
|
||||
ALog.d(TAG, String.format("合并文件耗时:%sms,合并后的文件长度:%s", (System.currentTimeMillis() - startTime),
|
||||
file.length()));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
32
README.md
32
README.md
@@ -44,16 +44,19 @@ Aria有以下特点:
|
||||
|
||||
## 引入库
|
||||
[](https://github.com/AriaLyy/Aria/blob/master/LICENSE)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
|
||||
|
||||
```java
|
||||
implementation 'com.arialyy.aria:core:3.8.3'
|
||||
annotationProcessor 'com.arialyy.aria:compiler:3.8.3'
|
||||
implementation 'com.arialyy.aria:ftpComponent:3.8.3' # 如果需要使用ftp,请增加该组件
|
||||
implementation 'com.arialyy.aria:m3u8Component:3.8.3' # 如果需要使用m3u8下载功能,请增加该组件
|
||||
implementation 'com.arialyy.aria:core:3.8.5'
|
||||
annotationProcessor 'com.arialyy.aria:compiler:3.8.5'
|
||||
implementation 'com.arialyy.aria:ftpComponent:3.8.5' # 如果需要使用ftp,请增加该组件
|
||||
implementation 'com.arialyy.aria:sftpComponent:3.8.5' # 如果需要使用ftp,请增加该组件
|
||||
implementation 'com.arialyy.aria:m3u8Component:3.8.5' # 如果需要使用m3u8下载功能,请增加该组件
|
||||
```
|
||||
如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:<last-version>'`替换为
|
||||
```
|
||||
@@ -139,14 +142,11 @@ protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
|
||||
### 版本日志
|
||||
+ v_3.8.3 (2020/1/9)
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/573
|
||||
- android P适配 https://github.com/AriaLyy/Aria/issues/581
|
||||
- 添加ftp服务器标志 https://github.com/AriaLyy/Aria/issues/580
|
||||
- 重构loader模块,让loader模块的代码更加清晰,去除一些不必要的线程创建
|
||||
- 修复ftp上传完成后,删除服务器端的文件,无法重新下载的问题
|
||||
- 增加获取执行中的任务api,详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html
|
||||
- 增加获取剩余时间的api,详情见:https://aria.laoyuyu.me/aria_doc/start/task_explain.html
|
||||
+ v_3.8.5 (2020/1/18)
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/599
|
||||
- 增加密钥url转换器的参数 https://github.com/AriaLyy/Aria/issues/603
|
||||
- 增加sftp,文件上传、下载功能,[sftp下载](https://aria.laoyuyu.me/aria_doc/download/sftp_normal.html),[sftp上传](https://aria.laoyuyu.me/aria_doc/upload/sftp_normal.html)
|
||||
- 使用零拷贝技术,优化了合并分块的效率
|
||||
|
||||
[更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md)
|
||||
|
||||
|
||||
11
SFtpComponent/bintray-release.gradle
Normal file
11
SFtpComponent/bintray-release.gradle
Normal file
@@ -0,0 +1,11 @@
|
||||
apply plugin: 'com.novoda.bintray-release'
|
||||
publish {
|
||||
artifactId = 'sftpComponent'
|
||||
uploadName = 'SFTPComponent'
|
||||
userOrg = rootProject.ext.userOrg
|
||||
groupId = rootProject.ext.groupId
|
||||
publishVersion = rootProject.ext.publishVersion
|
||||
desc = rootProject.ext.desc
|
||||
website = rootProject.ext.website
|
||||
licences = rootProject.ext.licences
|
||||
}
|
||||
@@ -25,8 +25,9 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation "com.jcraft:jsch:0.1.55"
|
||||
implementation "com.jcraft:jzlib:1.1.3"
|
||||
implementation project(path: ':FtpComponent')
|
||||
api "com.jcraft:jsch:0.1.55"
|
||||
api "com.jcraft:jzlib:1.1.3"
|
||||
implementation project(path: ':PublicComponent')
|
||||
}
|
||||
|
||||
apply from: 'bintray-release.gradle'
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.arialyy.aria.core.loader.IInfoTask;
|
||||
import com.arialyy.aria.core.loader.ILoaderVisitor;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
@@ -34,11 +33,11 @@ public abstract class AbsSFtpInfoTask<WP extends AbsTaskWrapper> implements IInf
|
||||
protected String TAG = CommonUtil.getClassName(this);
|
||||
protected Callback callback;
|
||||
private WP wrapper;
|
||||
private FtpTaskOption option;
|
||||
private SFtpTaskOption option;
|
||||
|
||||
public AbsSFtpInfoTask(WP wp) {
|
||||
this.wrapper = wp;
|
||||
this.option = (FtpTaskOption) wrapper.getTaskOption();
|
||||
this.option = (SFtpTaskOption) wrapper.getTaskOption();
|
||||
}
|
||||
|
||||
protected abstract void getFileInfo(Session session)
|
||||
@@ -65,7 +64,7 @@ public abstract class AbsSFtpInfoTask<WP extends AbsTaskWrapper> implements IInf
|
||||
}
|
||||
}
|
||||
|
||||
protected FtpTaskOption getOption() {
|
||||
protected SFtpTaskOption getOption() {
|
||||
return option;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public class SFtpSessionManager {
|
||||
if (session == null) {
|
||||
ALog.w(TAG, "从缓存获取session失败,key:" + key);
|
||||
}
|
||||
//cleanIdleSession();
|
||||
cleanIdleSession();
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,26 @@
|
||||
*/
|
||||
package com.arialyy.aria.sftp;
|
||||
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import com.arialyy.aria.core.inf.ITaskOption;
|
||||
|
||||
public class SFtpSubThreadCOnfig extends SubThreadConfig {
|
||||
public class SFtpTaskOption implements ITaskOption {
|
||||
|
||||
/**
|
||||
* 账号和密码
|
||||
*/
|
||||
private FtpUrlEntity urlEntity;
|
||||
|
||||
/**
|
||||
* 字符编码,默认为"utf-8"
|
||||
*/
|
||||
private String charSet = "utf-8";
|
||||
|
||||
public FtpUrlEntity getUrlEntity() {
|
||||
return urlEntity;
|
||||
}
|
||||
|
||||
public String getCharSet() {
|
||||
return charSet;
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class SFtpUtil {
|
||||
}
|
||||
}
|
||||
|
||||
setknowHost(jSch, entity);
|
||||
setKnowHost(jSch, entity);
|
||||
|
||||
Session session;
|
||||
if (TextUtils.isEmpty(entity.user)) {
|
||||
@@ -94,14 +94,14 @@ public class SFtpUtil {
|
||||
// 不检查公钥,需要在connect之前配置,但是不安全,no 模式会自动将配对信息写入know_host文件
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
session.setConfig(config);// 为Session对象设置properties
|
||||
session.setTimeout(3000);// 设置超时
|
||||
session.setTimeout(5000);// 设置超时
|
||||
session.setIdentityRepository(jSch.getIdentityRepository());
|
||||
session.connect();
|
||||
SFtpSessionManager.getInstance().addSession(session, threadId);
|
||||
return session;
|
||||
}
|
||||
|
||||
private void setknowHost(JSch jSch, FtpUrlEntity entity) throws JSchException {
|
||||
private void setKnowHost(JSch jSch, FtpUrlEntity entity) throws JSchException {
|
||||
IdEntity idEntity = entity.idEntity;
|
||||
if (idEntity.knowHost != null) {
|
||||
File knowFile = new File(idEntity.knowHost);
|
||||
|
||||
@@ -17,8 +17,10 @@ package com.arialyy.aria.sftp.download;
|
||||
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.sftp.AbsSFtpInfoTask;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
@@ -38,7 +40,7 @@ final class SFtpDInfoTask extends AbsSFtpInfoTask<DTaskWrapper> {
|
||||
|
||||
@Override protected void getFileInfo(Session session) throws JSchException,
|
||||
UnsupportedEncodingException, SftpException {
|
||||
FtpTaskOption option = (FtpTaskOption) getWrapper().getTaskOption();
|
||||
SFtpTaskOption option = (SFtpTaskOption) getWrapper().getTaskOption();
|
||||
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
|
||||
channel.connect(1000);
|
||||
|
||||
@@ -46,12 +48,23 @@ final class SFtpDInfoTask extends AbsSFtpInfoTask<DTaskWrapper> {
|
||||
//channel.setFilenameEncoding("gbk");
|
||||
|
||||
String remotePath = option.getUrlEntity().remotePath;
|
||||
String temp = CommonUtil.convertFtpChar(getOption().getCharSet(), remotePath);
|
||||
SftpATTRS attr = channel.stat(temp);
|
||||
getWrapper().getEntity().setFileSize(attr.getSize());
|
||||
CompleteInfo info = new CompleteInfo();
|
||||
info.code = 200;
|
||||
String temp = CommonUtil.convertSFtpChar(option.getCharSet(), remotePath);
|
||||
SftpATTRS attr = null;
|
||||
try {
|
||||
attr = channel.stat(temp);
|
||||
} catch (Exception e) {
|
||||
ALog.e(TAG, String.format("文件不存在,remotePath:%s", remotePath));
|
||||
}
|
||||
|
||||
if (attr != null) {
|
||||
getWrapper().getEntity().setFileSize(attr.getSize());
|
||||
CompleteInfo info = new CompleteInfo();
|
||||
info.code = 200;
|
||||
callback.onSucceed(getWrapper().getKey(), info);
|
||||
} else {
|
||||
callback.onFail(getWrapper().getEntity(),
|
||||
new AriaException(TAG, String.format("文件不存在,remotePath:%s", remotePath)), false);
|
||||
}
|
||||
channel.disconnect();
|
||||
callback.onSucceed(getWrapper().getKey(), info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ package com.arialyy.aria.sftp.download;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.AbsNormalEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.event.EventMsgUtil;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IDLoadListener;
|
||||
@@ -28,29 +29,27 @@ import com.arialyy.aria.core.loader.AbsNormalLoader;
|
||||
import com.arialyy.aria.core.loader.IInfoTask;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.loader.IThreadTaskBuilder;
|
||||
import com.arialyy.aria.core.loader.NormalTTBuilder;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
|
||||
final class SFtpDLoader extends AbsNormalLoader {
|
||||
final class SFtpDLoader extends AbsNormalLoader<DTaskWrapper> {
|
||||
|
||||
private int startThreadNum; //启动的线程数
|
||||
private boolean isComplete = false;
|
||||
private Looper looper;
|
||||
|
||||
SFtpDLoader(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
SFtpDLoader(DTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
mTempFile = new File(getEntity().getFilePath());
|
||||
EventMsgUtil.getDefault().register(this);
|
||||
setUpdateInterval(wrapper.getConfig().getUpdateInterval());
|
||||
}
|
||||
|
||||
private AbsNormalEntity getEntity() {
|
||||
return (AbsNormalEntity) mTaskWrapper.getEntity();
|
||||
private DownloadEntity getEntity() {
|
||||
return mTaskWrapper.getEntity();
|
||||
}
|
||||
|
||||
@Override public long getFileSize() {
|
||||
|
||||
@@ -23,8 +23,7 @@ import com.arialyy.aria.core.loader.LoaderStructure;
|
||||
import com.arialyy.aria.core.loader.NormalTTBuilder;
|
||||
import com.arialyy.aria.core.loader.NormalThreadStateManager;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
import com.arialyy.aria.ftp.download.FtpDRecordHandler;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
|
||||
/**
|
||||
* sftp下载工具
|
||||
@@ -35,16 +34,17 @@ public class SFtpDLoaderUtil extends AbsNormalLoaderUtil {
|
||||
|
||||
public SFtpDLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
wrapper.generateTaskOption(FtpTaskOption.class);
|
||||
wrapper.generateTaskOption(SFtpTaskOption.class);
|
||||
}
|
||||
|
||||
@Override public AbsNormalLoader getLoader() {
|
||||
return mLoader == null ? new SFtpDLoader(getTaskWrapper(), getListener()) : mLoader;
|
||||
return mLoader == null ? new SFtpDLoader((DTaskWrapper) getTaskWrapper(), getListener())
|
||||
: mLoader;
|
||||
}
|
||||
|
||||
@Override public LoaderStructure BuildLoaderStructure() {
|
||||
LoaderStructure structure = new LoaderStructure();
|
||||
structure.addComponent(new FtpDRecordHandler((DTaskWrapper) getTaskWrapper()))
|
||||
structure.addComponent(new SFtpDRecordHandler((DTaskWrapper) getTaskWrapper()))
|
||||
.addComponent(new NormalThreadStateManager(getListener()))
|
||||
.addComponent(new SFtpDInfoTask((DTaskWrapper) getTaskWrapper()))
|
||||
.addComponent(new NormalTTBuilder(getTaskWrapper(), new SFtpDTTBuilderAdapter(
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.sftp.download;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.RecordHandler;
|
||||
import com.arialyy.aria.core.common.RecordHelper;
|
||||
import com.arialyy.aria.core.config.Configuration;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.RecordUtil;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @Author lyy
|
||||
* @Date 2019-09-19
|
||||
*/
|
||||
final class SFtpDRecordHandler extends RecordHandler {
|
||||
|
||||
SFtpDRecordHandler(DTaskWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
@Override public void handlerTaskRecord(TaskRecord record) {
|
||||
RecordHelper helper = new RecordHelper(getWrapper(), record);
|
||||
if (record.threadNum == 1) {
|
||||
helper.handleSingleThreadRecord();
|
||||
} else {
|
||||
if (getWrapper().isSupportBP()) {
|
||||
if (record.isBlock) {
|
||||
helper.handleBlockRecord();
|
||||
} else {
|
||||
helper.handleMultiRecord();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) {
|
||||
ThreadRecord tr;
|
||||
tr = new ThreadRecord();
|
||||
tr.taskKey = record.filePath;
|
||||
tr.threadId = threadId;
|
||||
tr.startLocation = startL;
|
||||
tr.isComplete = false;
|
||||
tr.threadType = record.taskType;
|
||||
//最后一个线程的结束位置即为文件的总长度
|
||||
if (threadId == (record.threadNum - 1)) {
|
||||
endL = getFileSize();
|
||||
}
|
||||
tr.endLocation = endL;
|
||||
tr.blockLen = RecordUtil.getBlockLen(getFileSize(), threadId, record.threadNum);
|
||||
return tr;
|
||||
}
|
||||
|
||||
@Override public TaskRecord createTaskRecord(int threadNum) {
|
||||
TaskRecord record = new TaskRecord();
|
||||
record.fileName = getEntity().getFileName();
|
||||
record.filePath = getEntity().getFilePath();
|
||||
record.threadRecords = new ArrayList<>();
|
||||
record.threadNum = threadNum;
|
||||
record.isBlock = threadNum > 1;
|
||||
record.taskType = ITaskWrapper.D_SFTP;
|
||||
record.isGroupRecord = false;
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override public int initTaskThreadNum() {
|
||||
int threadNum = Configuration.getInstance().downloadCfg.getThreadNum();
|
||||
return getFileSize() <= IRecordHandler.SUB_LEN
|
||||
|| threadNum == 1
|
||||
? 1
|
||||
: threadNum;
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,8 @@ import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.loader.AbsNormalTTBuilderAdapter;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.task.IThreadTaskAdapter;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
import com.arialyy.aria.sftp.SFtpSessionManager;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
import com.arialyy.aria.sftp.SFtpUtil;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
@@ -35,11 +35,11 @@ import com.jcraft.jsch.Session;
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
class SFtpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
|
||||
private FtpTaskOption option;
|
||||
final class SFtpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
|
||||
private SFtpTaskOption option;
|
||||
|
||||
SFtpDTTBuilderAdapter(DTaskWrapper wrapper) {
|
||||
option = (FtpTaskOption) wrapper.getTaskOption();
|
||||
option = (SFtpTaskOption) wrapper.getTaskOption();
|
||||
}
|
||||
|
||||
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {
|
||||
@@ -71,6 +71,7 @@ class SFtpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
|
||||
}
|
||||
|
||||
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
|
||||
|
||||
if (!record.isBlock) {
|
||||
if (getTempFile().exists()) {
|
||||
FileUtil.deleteFile(getTempFile());
|
||||
|
||||
@@ -18,18 +18,22 @@ package com.arialyy.aria.sftp.download;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
import com.jcraft.jsch.SftpProgressMonitor;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
/**
|
||||
* sftp 线程任务适配器
|
||||
@@ -39,12 +43,12 @@ import java.nio.channels.FileChannel;
|
||||
final class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
private ChannelSftp channelSftp;
|
||||
private Session session;
|
||||
private FtpTaskOption option;
|
||||
private SFtpTaskOption option;
|
||||
|
||||
SFtpDThreadTaskAdapter(SubThreadConfig config) {
|
||||
super(config);
|
||||
session = (Session) config.obj;
|
||||
option = (FtpTaskOption) getTaskWrapper().getTaskOption();
|
||||
option = (SFtpTaskOption) getTaskWrapper().getTaskOption();
|
||||
}
|
||||
|
||||
@Override protected void handlerThreadTask() {
|
||||
@@ -52,7 +56,6 @@ final class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
fail(new AriaException(TAG, "session 为空"), false);
|
||||
return;
|
||||
}
|
||||
FileOutputStream fos;
|
||||
try {
|
||||
int timeout = getTaskConfig().getConnectTimeOut();
|
||||
if (!session.isConnected()) {
|
||||
@@ -60,10 +63,6 @@ final class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
}
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect(timeout);
|
||||
fos = new FileOutputStream(getThreadConfig().tempFile, true);
|
||||
if (channelSftp.isClosed() || !channelSftp.isConnected()) {
|
||||
channelSftp.connect();
|
||||
}
|
||||
ALog.d(TAG,
|
||||
String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", getTaskWrapper().getKey(),
|
||||
getThreadRecord().threadId, getThreadRecord().startLocation,
|
||||
@@ -72,26 +71,66 @@ final class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
|
||||
String charSet = option.getCharSet();
|
||||
String remotePath =
|
||||
CommonUtil.convertFtpChar(charSet, option.getUrlEntity().remotePath);
|
||||
if (getThreadRecord().startLocation > 0) {
|
||||
channelSftp.get(remotePath, fos, new Monitor(true), ChannelSftp.RESUME,
|
||||
getThreadRecord().startLocation);
|
||||
} else {
|
||||
channelSftp.get(remotePath, fos, new Monitor(false));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CommonUtil.convertSFtpChar(charSet, option.getUrlEntity().remotePath);
|
||||
download(remotePath);
|
||||
} catch (SftpException e) {
|
||||
e.printStackTrace();
|
||||
ALog.e(TAG, "错误类型:" + e.id);
|
||||
fail(null, false);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
fail(null, false);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
fail(null, true);
|
||||
} catch (JSchException e) {
|
||||
e.printStackTrace();
|
||||
fail(null, false);
|
||||
} finally {
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*/
|
||||
private void download(String remotePath) throws SftpException, IOException {
|
||||
InputStream is =
|
||||
channelSftp.get(remotePath, new Monitor(), getThreadRecord().startLocation);
|
||||
FileOutputStream fos = new FileOutputStream(getThreadConfig().tempFile, true);
|
||||
FileChannel foc = fos.getChannel();
|
||||
ReadableByteChannel fic = Channels.newChannel(is);
|
||||
ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize());
|
||||
int len;
|
||||
while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) {
|
||||
if (getThreadTask().isBreak()) {
|
||||
break;
|
||||
}
|
||||
if (mSpeedBandUtil != null) {
|
||||
mSpeedBandUtil.limitNextBytes(len);
|
||||
}
|
||||
if (getRangeProgress() + len >= getThreadRecord().endLocation) {
|
||||
len = (int) (getThreadRecord().endLocation - getRangeProgress());
|
||||
bf.flip();
|
||||
fos.write(bf.array(), 0, len);
|
||||
bf.compact();
|
||||
progress(len);
|
||||
break;
|
||||
} else {
|
||||
bf.flip();
|
||||
foc.write(bf);
|
||||
bf.compact();
|
||||
progress(len);
|
||||
}
|
||||
}
|
||||
fos.flush();
|
||||
fos.close();
|
||||
is.close();
|
||||
}
|
||||
|
||||
private class Monitor implements SftpProgressMonitor {
|
||||
|
||||
private boolean isResume;
|
||||
|
||||
private Monitor(boolean isResume) {
|
||||
this.isResume = isResume;
|
||||
private Monitor() {
|
||||
}
|
||||
|
||||
@Override public void init(int op, String src, String dest, long max) {
|
||||
@@ -104,19 +143,10 @@ final class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
*/
|
||||
@Override public boolean count(long count) {
|
||||
|
||||
if (mSpeedBandUtil != null) {
|
||||
mSpeedBandUtil.limitNextBytes((int) count);
|
||||
}
|
||||
|
||||
/*
|
||||
* jsch 如果是恢复任务,第一次回调count会将已下载的长度返回,后面才是新增的文件长度。
|
||||
* 所以恢复任务的话,需要忽略一次回调
|
||||
*/
|
||||
if (!isResume) {
|
||||
progress(count);
|
||||
}
|
||||
isResume = false;
|
||||
//return !getThreadTask().isBreak() && getRangeProgress() < getThreadRecord().endLocation;
|
||||
if (getRangeProgress() > getThreadRecord().endLocation) {
|
||||
return false;
|
||||
}
|
||||
@@ -129,62 +159,6 @@ final class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
}
|
||||
|
||||
complete();
|
||||
|
||||
//boolean isSuccess = true;
|
||||
//// 剪裁文件
|
||||
//if (getRangeProgress() > getThreadRecord().endLocation) {
|
||||
// isSuccess = clipFile();
|
||||
//}
|
||||
//if (isSuccess) {
|
||||
// complete();
|
||||
//} else {
|
||||
// fail(new AriaException(TAG, "剪切文件失败"), false);
|
||||
//}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件超出内容,剪切文件
|
||||
*
|
||||
* @return true 剪切文件成功
|
||||
*/
|
||||
private boolean clipFile() {
|
||||
FileInputStream fis = null;
|
||||
FileOutputStream fos = null;
|
||||
long stime = System.currentTimeMillis();
|
||||
try {
|
||||
String destPath = getThreadConfig().tempFile.getPath();
|
||||
ALog.d(TAG, "oldSize = " + getThreadConfig().tempFile.length());
|
||||
String tempPath = destPath + "_temp";
|
||||
fis = new FileInputStream(getThreadConfig().tempFile);
|
||||
fos = new FileOutputStream(tempPath);
|
||||
FileChannel inChannel = fis.getChannel();
|
||||
FileChannel outChannel = fos.getChannel();
|
||||
inChannel.transferTo(0, getThreadRecord().endLocation, outChannel);
|
||||
|
||||
FileUtil.deleteFile(getThreadConfig().tempFile);
|
||||
File oldF = new File(tempPath);
|
||||
File newF = new File(destPath);
|
||||
boolean b = oldF.renameTo(newF);
|
||||
ALog.d(TAG, String.format("剪裁文件消耗:%sms,fileSize:%s,threadId:%s",
|
||||
(System.currentTimeMillis() - stime), newF.length(),
|
||||
getThreadConfig().record.threadId));
|
||||
return b;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
|
||||
if (fis != null) {
|
||||
fis.close();
|
||||
}
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.sftp.upload;
|
||||
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.sftp.AbsSFtpInfoTask;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
final class SFtpUInfoTask extends AbsSFtpInfoTask<UTaskWrapper> {
|
||||
static final int ISCOMPLETE = 0xa1;
|
||||
|
||||
SFtpUInfoTask(UTaskWrapper uTaskWrapper) {
|
||||
super(uTaskWrapper);
|
||||
}
|
||||
|
||||
@Override protected void getFileInfo(Session session)
|
||||
throws JSchException, UnsupportedEncodingException, SftpException {
|
||||
SFtpTaskOption option = (SFtpTaskOption) getWrapper().getTaskOption();
|
||||
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
|
||||
channel.connect(1000);
|
||||
|
||||
String remotePath = option.getUrlEntity().remotePath;
|
||||
String temp = CommonUtil.convertSFtpChar(getOption().getCharSet(), remotePath)
|
||||
+ "/"
|
||||
+ getWrapper().getEntity().getFileName();
|
||||
|
||||
SftpATTRS attr = null;
|
||||
try {
|
||||
attr = channel.stat(temp);
|
||||
} catch (Exception e) {
|
||||
ALog.d(TAG, String.format("文件不存在,remotePath:%s", remotePath));
|
||||
}
|
||||
|
||||
boolean isComplete = false;
|
||||
UploadEntity entity = getWrapper().getEntity();
|
||||
if (attr != null && attr.getSize() == entity.getFileSize()) {
|
||||
isComplete = true;
|
||||
}
|
||||
|
||||
CompleteInfo info = new CompleteInfo();
|
||||
info.code = isComplete ? ISCOMPLETE : 200;
|
||||
info.obj = attr;
|
||||
channel.disconnect();
|
||||
callback.onSucceed(getWrapper().getKey(), info);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.sftp.upload;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.event.EventMsgUtil;
|
||||
import com.arialyy.aria.core.inf.IThreadStateManager;
|
||||
import com.arialyy.aria.core.listener.IDLoadListener;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoader;
|
||||
import com.arialyy.aria.core.loader.IInfoTask;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.loader.IThreadTaskBuilder;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.exception.BaseException;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import java.io.File;
|
||||
|
||||
final class SFtpULoader extends AbsNormalLoader<UTaskWrapper> {
|
||||
|
||||
private Looper looper;
|
||||
|
||||
SFtpULoader(UTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
mTempFile = new File(getEntity().getFilePath());
|
||||
EventMsgUtil.getDefault().register(this);
|
||||
setUpdateInterval(wrapper.getConfig().getUpdateInterval());
|
||||
}
|
||||
|
||||
private UploadEntity getEntity() {
|
||||
return mTaskWrapper.getEntity();
|
||||
}
|
||||
|
||||
@Override public long getFileSize() {
|
||||
return getEntity().getFileSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大下载/上传速度AbsFtpInfoThread
|
||||
*
|
||||
* @param maxSpeed 单位为:kb
|
||||
*/
|
||||
protected void setMaxSpeed(int maxSpeed) {
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
if (threadTask != null) {
|
||||
threadTask.setMaxSpeed(maxSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventMsgUtil.getDefault().unRegister(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动单线程任务
|
||||
*/
|
||||
@Override
|
||||
public void handleTask(Looper looper) {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
this.looper = looper;
|
||||
mInfoTask.run();
|
||||
}
|
||||
|
||||
private void startThreadTask(SftpATTRS attrs) {
|
||||
|
||||
if (getListener() instanceof IDLoadListener) {
|
||||
((IDLoadListener) getListener()).onPostPre(getEntity().getFileSize());
|
||||
}
|
||||
File file = new File(getEntity().getFilePath());
|
||||
if (file.getParentFile() != null && !file.getParentFile().exists()) {
|
||||
FileUtil.createDir(file.getPath());
|
||||
}
|
||||
// 处理记录、初始化状态管理器
|
||||
SFtpURecordHandler recordHandler = (SFtpURecordHandler) mRecordHandler;
|
||||
recordHandler.setFtpAttrs(attrs);
|
||||
mRecord = recordHandler.getRecord(getFileSize());
|
||||
mStateManager.setLooper(mRecord, looper);
|
||||
|
||||
// 创建线程任务
|
||||
getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord,
|
||||
new Handler(looper, mStateManager.getHandlerCallback())));
|
||||
|
||||
mStateManager.updateCurrentProgress(getEntity().getCurrentProgress());
|
||||
if (mStateManager.getCurrentProgress() > 0) {
|
||||
getListener().onResume(mStateManager.getCurrentProgress());
|
||||
} else {
|
||||
getListener().onStart(mStateManager.getCurrentProgress());
|
||||
}
|
||||
|
||||
// 启动线程任务
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask);
|
||||
}
|
||||
|
||||
// 启动定时器
|
||||
startTimer();
|
||||
}
|
||||
|
||||
@Override public long getCurrentProgress() {
|
||||
return isRunning() ? mStateManager.getCurrentProgress() : getEntity().getCurrentProgress();
|
||||
}
|
||||
|
||||
@Override public void addComponent(IRecordHandler recordHandler) {
|
||||
mRecordHandler = recordHandler;
|
||||
}
|
||||
|
||||
@Override public void addComponent(IInfoTask infoTask) {
|
||||
mInfoTask = infoTask;
|
||||
infoTask.setCallback(new IInfoTask.Callback() {
|
||||
@Override public void onSucceed(String key, CompleteInfo info) {
|
||||
if (info.code == SFtpUInfoTask.ISCOMPLETE) {
|
||||
getListener().onComplete();
|
||||
} else {
|
||||
startThreadTask((SftpATTRS) info.obj);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
|
||||
getListener().onFail(needRetry, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadStateManager threadState) {
|
||||
mStateManager = threadState;
|
||||
}
|
||||
|
||||
@Override public void addComponent(IThreadTaskBuilder builder) {
|
||||
mTTBuilder = builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.sftp.upload;
|
||||
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoader;
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
|
||||
import com.arialyy.aria.core.loader.LoaderStructure;
|
||||
import com.arialyy.aria.core.loader.NormalTTBuilder;
|
||||
import com.arialyy.aria.core.loader.NormalThreadStateManager;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
|
||||
/**
|
||||
* sftp下载工具
|
||||
*
|
||||
* @author lyy
|
||||
*/
|
||||
public class SFtpULoaderUtil extends AbsNormalLoaderUtil {
|
||||
|
||||
public SFtpULoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
wrapper.generateTaskOption(SFtpTaskOption.class);
|
||||
}
|
||||
|
||||
@Override public AbsNormalLoader getLoader() {
|
||||
return mLoader == null ? new SFtpULoader((UTaskWrapper) getTaskWrapper(), getListener())
|
||||
: mLoader;
|
||||
}
|
||||
|
||||
@Override public LoaderStructure BuildLoaderStructure() {
|
||||
LoaderStructure structure = new LoaderStructure();
|
||||
structure.addComponent(new SFtpURecordHandler((UTaskWrapper) getTaskWrapper()))
|
||||
.addComponent(new NormalThreadStateManager(getListener()))
|
||||
.addComponent(new SFtpUInfoTask((UTaskWrapper) getTaskWrapper()))
|
||||
.addComponent(new NormalTTBuilder(getTaskWrapper(), new SFtpUTTBuilderAdapter(
|
||||
(UTaskWrapper) getTaskWrapper())));
|
||||
structure.accept(getLoader());
|
||||
return structure;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.sftp.upload;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.RecordHandler;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.core.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.RecordUtil;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 上传任务记录处理器
|
||||
*/
|
||||
final class SFtpURecordHandler extends RecordHandler {
|
||||
private SftpATTRS ftpAttrs;
|
||||
|
||||
SFtpURecordHandler(UTaskWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
void setFtpAttrs(SftpATTRS ftpAttrs) {
|
||||
this.ftpAttrs = ftpAttrs;
|
||||
}
|
||||
|
||||
@Override public void handlerTaskRecord(TaskRecord record) {
|
||||
if (record.threadRecords == null || record.threadRecords.isEmpty()) {
|
||||
record.threadRecords = new ArrayList<>();
|
||||
record.threadRecords.add(
|
||||
createThreadRecord(record, 0, ftpAttrs == null ? 0 : ftpAttrs.getSize(), getFileSize()));
|
||||
}
|
||||
|
||||
if (ftpAttrs != null) {
|
||||
UploadEntity entity = (UploadEntity) getWrapper().getEntity();
|
||||
//远程文件已完成
|
||||
if (ftpAttrs.getSize() == getFileSize()) {
|
||||
record.threadRecords.get(0).isComplete = true;
|
||||
ALog.d(TAG, "FTP服务器上已存在该文件【" + entity.getFileName() + "】");
|
||||
} else if (ftpAttrs.getSize() == 0) {
|
||||
getWrapper().setNewTask(true);
|
||||
ALog.d(TAG, "FTP服务器上已存在该文件【" + entity.getFileName() + "】,但文件长度为0,重新上传该文件");
|
||||
} else {
|
||||
ALog.w(TAG, "FTP服务器已存在未完成的文件【"
|
||||
+ entity.getFileName()
|
||||
+ ",size: "
|
||||
+ ftpAttrs.getSize()
|
||||
+ "】"
|
||||
+ "尝试从位置:"
|
||||
+ (ftpAttrs.getSize() - 1)
|
||||
+ "开始上传");
|
||||
getWrapper().setNewTask(false);
|
||||
|
||||
// 修改记录
|
||||
ThreadRecord threadRecord = record.threadRecords.get(0);
|
||||
//修改本地保存的停止地址为服务器上对应文件的大小
|
||||
threadRecord.startLocation = ftpAttrs.getSize() - 1;
|
||||
}
|
||||
} else {
|
||||
ALog.d(TAG, "SFTP服务器上不存在该文件");
|
||||
getWrapper().setNewTask(true);
|
||||
ThreadRecord tr = record.threadRecords.get(0);
|
||||
tr.startLocation = 0;
|
||||
tr.endLocation = getFileSize();
|
||||
tr.isComplete = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) {
|
||||
ThreadRecord tr;
|
||||
tr = new ThreadRecord();
|
||||
tr.taskKey = record.filePath;
|
||||
tr.threadId = threadId;
|
||||
tr.startLocation = startL;
|
||||
tr.isComplete = false;
|
||||
tr.threadType = record.taskType;
|
||||
tr.endLocation = getFileSize();
|
||||
tr.blockLen = RecordUtil.getBlockLen(getFileSize(), threadId, record.threadNum);
|
||||
return tr;
|
||||
}
|
||||
|
||||
@Override public TaskRecord createTaskRecord(int threadNum) {
|
||||
TaskRecord record = new TaskRecord();
|
||||
record.fileName = getEntity().getFileName();
|
||||
record.filePath = getEntity().getFilePath();
|
||||
record.threadRecords = new ArrayList<>();
|
||||
record.threadNum = threadNum;
|
||||
record.isBlock = false;
|
||||
record.taskType = ITaskWrapper.U_SFTP;
|
||||
record.isGroupRecord = getEntity().isGroupChild();
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override public int initTaskThreadNum() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.sftp.upload;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.ThreadRecord;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.loader.AbsNormalTTBuilderAdapter;
|
||||
import com.arialyy.aria.core.task.IThreadTaskAdapter;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.sftp.SFtpSessionManager;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
import com.arialyy.aria.sftp.SFtpUtil;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
final class SFtpUTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
|
||||
private SFtpTaskOption option;
|
||||
|
||||
SFtpUTTBuilderAdapter(UTaskWrapper wrapper) {
|
||||
option = (SFtpTaskOption) wrapper.getTaskOption();
|
||||
}
|
||||
|
||||
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {
|
||||
return new SFtpUThreadTaskAdapter(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SubThreadConfig getSubThreadConfig(Handler stateHandler, ThreadRecord threadRecord,
|
||||
boolean isBlock, int startNum) {
|
||||
SubThreadConfig config =
|
||||
super.getSubThreadConfig(stateHandler, threadRecord, isBlock, startNum);
|
||||
|
||||
FtpUrlEntity entity = option.getUrlEntity();
|
||||
String key =
|
||||
CommonUtil.getStrMd5(entity.hostName + entity.port + entity.user + threadRecord.threadId);
|
||||
Session session = SFtpSessionManager.getInstance().getSession(key);
|
||||
if (session == null) {
|
||||
try {
|
||||
session = SFtpUtil.getInstance().getSession(entity, threadRecord.threadId);
|
||||
} catch (JSchException e) {
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
config.obj = session;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.sftp.upload;
|
||||
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.sftp.SFtpTaskOption;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.BufferedRandomAccessFile;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
import com.jcraft.jsch.SftpProgressMonitor;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* sftp 线程任务适配器
|
||||
*
|
||||
* @author lyy
|
||||
*/
|
||||
final class SFtpUThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
private ChannelSftp channelSftp;
|
||||
private Session session;
|
||||
private SFtpTaskOption option;
|
||||
|
||||
SFtpUThreadTaskAdapter(SubThreadConfig config) {
|
||||
super(config);
|
||||
session = (Session) config.obj;
|
||||
option = (SFtpTaskOption) getTaskWrapper().getTaskOption();
|
||||
}
|
||||
|
||||
@Override protected void handlerThreadTask() {
|
||||
if (session == null) {
|
||||
fail(new AriaException(TAG, "session 为空"), false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ALog.d(TAG,
|
||||
String.format("任务【%s】线程__%s__开始上传【开始位置 : %s,结束位置:%s】", getTaskWrapper().getKey(),
|
||||
getThreadRecord().threadId, getThreadRecord().startLocation,
|
||||
getThreadRecord().endLocation));
|
||||
int timeout = getTaskConfig().getConnectTimeOut();
|
||||
if (!session.isConnected()) {
|
||||
session.connect(timeout);
|
||||
}
|
||||
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
|
||||
String charSet = option.getCharSet();
|
||||
String remotePath =
|
||||
CommonUtil.convertSFtpChar(charSet, option.getUrlEntity().remotePath);
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect(timeout);
|
||||
|
||||
if (!dirIsExist(remotePath)) {
|
||||
createDir(remotePath);
|
||||
}
|
||||
channelSftp.cd(remotePath);
|
||||
upload(remotePath);
|
||||
} catch (JSchException e) {
|
||||
e.printStackTrace();
|
||||
fail(null, false);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
fail(null, false);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
fail(null, true);
|
||||
} catch (SftpException e) {
|
||||
e.printStackTrace();
|
||||
fail(null, false);
|
||||
ALog.d(TAG, "错误类型:" + e.id);
|
||||
} finally {
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程文件夹是否存在
|
||||
*
|
||||
* @return true 文件夹存在
|
||||
*/
|
||||
private boolean dirIsExist(String remotePath) {
|
||||
try {
|
||||
channelSftp.ls(remotePath);
|
||||
} catch (SftpException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件夹
|
||||
*
|
||||
* @throws SftpException
|
||||
*/
|
||||
private void createDir(String remotePath) throws SftpException {
|
||||
String[] folders = remotePath.split("/");
|
||||
|
||||
for (String folder : folders) {
|
||||
if (folder.length() > 0) {
|
||||
try {
|
||||
channelSftp.cd(folder);
|
||||
} catch (SftpException e) {
|
||||
channelSftp.mkdir(folder);
|
||||
channelSftp.cd(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复上传
|
||||
*
|
||||
* @throws SftpException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void upload(String remotePath) throws SftpException, IOException {
|
||||
UploadEntity entity = (UploadEntity) getTaskWrapper().getEntity();
|
||||
remotePath = remotePath.concat("/").concat(entity.getFileName());
|
||||
BufferedRandomAccessFile brf = new BufferedRandomAccessFile(getThreadConfig().tempFile, "r");
|
||||
int mode = ChannelSftp.OVERWRITE;
|
||||
boolean isResume = false;
|
||||
if (getThreadRecord().startLocation > 0) {
|
||||
brf.seek(getThreadRecord().startLocation);
|
||||
mode = ChannelSftp.APPEND;
|
||||
isResume = true;
|
||||
}
|
||||
OutputStream os = channelSftp.put(remotePath, new Monitor(isResume), mode);
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = brf.read(buffer)) != -1) {
|
||||
if (getThreadTask().isBreak()) {
|
||||
break;
|
||||
}
|
||||
os.write(buffer, 0, bytesRead);
|
||||
if (mSpeedBandUtil != null) {
|
||||
mSpeedBandUtil.limitNextBytes(bytesRead);
|
||||
}
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
brf.close();
|
||||
}
|
||||
|
||||
private class Monitor implements SftpProgressMonitor {
|
||||
|
||||
private boolean isResume;
|
||||
|
||||
private Monitor(boolean isResume) {
|
||||
this.isResume = isResume;
|
||||
}
|
||||
|
||||
@Override public void init(int op, String src, String dest, long max) {
|
||||
ALog.d(TAG, String.format("op = %s; src = %s; dest = %s; max = %s", op, src, dest, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param count 已传输的数据
|
||||
* @return false 取消任务
|
||||
*/
|
||||
@Override public boolean count(long count) {
|
||||
|
||||
/*
|
||||
* jsch 如果是恢复任务,第一次回调count会将已下载的长度返回,后面才是新增的文件长度。
|
||||
* 所以恢复任务的话,需要忽略一次回调
|
||||
*/
|
||||
if (!isResume) {
|
||||
progress(count);
|
||||
}
|
||||
isResume = false;
|
||||
|
||||
return !getThreadTask().isBreak();
|
||||
}
|
||||
|
||||
@Override public void end() {
|
||||
if (getThreadTask().isBreak()) {
|
||||
return;
|
||||
}
|
||||
|
||||
complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,6 @@
|
||||
android:sharedUserId="com.arialyy"
|
||||
package="com.arialyy.simple">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<application
|
||||
android:name="com.arialyy.simple.base.BaseApplication"
|
||||
android:allowBackup="true"
|
||||
@@ -26,35 +21,34 @@
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".core.download.SFtpDownloadActivity"/>
|
||||
<activity android:name=".core.download.DownloadActivity"/>
|
||||
<activity android:name=".core.download.SingleTaskActivity"/>
|
||||
<activity android:name=".core.download.mutil.MultiTaskActivity"/>
|
||||
<activity android:name=".core.download.fragment.FragmentActivity"/>
|
||||
<activity android:name=".core.download.mutil.MultiDownloadActivity"/>
|
||||
<activity android:name=".core.download.HighestPriorityActivity"/>
|
||||
<activity android:name=".core.download.group.DownloadGroupActivity"/>
|
||||
<activity android:name=".core.download.FtpDownloadActivity"/>
|
||||
<activity android:name=".core.download.group.FTPDirDownloadActivity"/>
|
||||
<activity android:name=".core.upload.HttpUploadActivity"/>
|
||||
<activity android:name=".core.upload.FtpUploadActivity"/>
|
||||
<activity android:name=".core.download.KotlinDownloadActivity"/>
|
||||
<activity android:name=".core.download.ComponentActivity"/>
|
||||
<activity android:name=".core.upload.SFtpUploadActivity" />
|
||||
<activity android:name=".core.download.SFtpDownloadActivity" />
|
||||
<activity android:name=".core.download.DownloadActivity" />
|
||||
<activity android:name=".core.download.SingleTaskActivity" />
|
||||
<activity android:name=".core.download.mutil.MultiTaskActivity" />
|
||||
<activity android:name=".core.download.fragment.FragmentActivity" />
|
||||
<activity android:name=".core.download.mutil.MultiDownloadActivity" />
|
||||
<activity android:name=".core.download.HighestPriorityActivity" />
|
||||
<activity android:name=".core.download.group.DownloadGroupActivity" />
|
||||
<activity android:name=".core.download.FtpDownloadActivity" />
|
||||
<activity android:name=".core.download.group.FTPDirDownloadActivity" />
|
||||
<activity android:name=".core.upload.HttpUploadActivity" />
|
||||
<activity android:name=".core.upload.FtpUploadActivity" />
|
||||
<activity android:name=".core.download.KotlinDownloadActivity" />
|
||||
<activity android:name=".core.download.ComponentActivity" />
|
||||
<activity
|
||||
android:name=".core.FullScreenCodeActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:theme="@style/FullScreen"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize"/>
|
||||
<activity android:name=".core.download.m3u8.M3U8VodDLoadActivity"/>
|
||||
<activity android:name=".core.download.m3u8.M3U8LiveDLoadActivity"/>
|
||||
|
||||
<service android:name=".core.download.service.DownloadService"/>
|
||||
android:windowSoftInputMode="stateHidden|adjustResize" />
|
||||
<activity android:name=".core.download.m3u8.M3U8VodDLoadActivity" />
|
||||
<activity android:name=".core.download.m3u8.M3U8LiveDLoadActivity" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
@@ -63,8 +57,15 @@
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/aria_fileprovider_paths"/>
|
||||
android:resource="@xml/aria_fileprovider_paths" />
|
||||
</provider>
|
||||
|
||||
<service android:name=".core.download.service.DownloadService" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
3、只对新的多线程下载任务有效
|
||||
4、只对多线程的任务有效
|
||||
-->
|
||||
<useBlock value="true"/>
|
||||
<useBlock value="false"/>
|
||||
|
||||
<!--设置下载线程数,下载线程数不能小于1
|
||||
注意:
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.arialyy.simple.core.download.m3u8.M3U8LiveDLoadActivity;
|
||||
import com.arialyy.simple.core.download.m3u8.M3U8VodDLoadActivity;
|
||||
import com.arialyy.simple.core.upload.FtpUploadActivity;
|
||||
import com.arialyy.simple.core.upload.HttpUploadActivity;
|
||||
import com.arialyy.simple.core.upload.SFtpUploadActivity;
|
||||
import com.arialyy.simple.databinding.ActivityMainBinding;
|
||||
import com.arialyy.simple.modlue.CommonModule;
|
||||
import com.arialyy.simple.to.NormalTo;
|
||||
@@ -117,10 +118,14 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> {
|
||||
module.startNextActivity(MainActivity.this, data.get(position),
|
||||
M3U8LiveDLoadActivity.class);
|
||||
break;
|
||||
case 8: // sftp
|
||||
case 8: // d_sftp
|
||||
module.startNextActivity(MainActivity.this, data.get(position),
|
||||
SFtpDownloadActivity.class);
|
||||
break;
|
||||
case 9: // u_sftp
|
||||
module.startNextActivity(MainActivity.this, data.get(position),
|
||||
SFtpUploadActivity.class);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import com.arialyy.annotations.Download;
|
||||
import com.arialyy.aria.core.Aria;
|
||||
import com.arialyy.aria.core.common.FtpOption;
|
||||
import com.arialyy.aria.core.common.SFtpOption;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.task.DownloadTask;
|
||||
@@ -105,7 +105,6 @@ public class SFtpDownloadActivity extends BaseActivity<ActivitySftpDownloadBindi
|
||||
if (!pubKey.exists()) {
|
||||
FileUtil.createFileFormInputStream(getAssets().open("id_rsa.pub"), pubKeyPath);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -144,8 +143,8 @@ public class SFtpDownloadActivity extends BaseActivity<ActivitySftpDownloadBindi
|
||||
}
|
||||
}
|
||||
|
||||
private FtpOption getFtpOption() {
|
||||
FtpOption option = new FtpOption();
|
||||
private SFtpOption getFtpOption() {
|
||||
SFtpOption option = new SFtpOption();
|
||||
option.login(user, passw); // 账号密码登录
|
||||
// 证书登录
|
||||
option.setPrvKey(prvKeyPath); // 设置私钥
|
||||
|
||||
@@ -237,6 +237,7 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
|
||||
|
||||
@Download.onTaskFail
|
||||
void taskFail(DownloadTask task, Exception e) {
|
||||
ALog.d(TAG, "下载失败");
|
||||
Toast.makeText(SingleTaskActivity.this, getString(R.string.download_fail), Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
if (task != null && task.getKey().equals(mUrl)) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
@@ -76,6 +77,9 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
if (entity.getM3U8Entity() != null){
|
||||
getBinding().pb.setMax(entity.getM3U8Entity().getPeerNum());
|
||||
}
|
||||
mTaskId = entity.getId();
|
||||
if (entity.getState() == IEntity.STATE_STOP) {
|
||||
getBinding().setStateStr(getString(R.string.resume));
|
||||
@@ -98,6 +102,19 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
|
||||
}
|
||||
});
|
||||
getBinding().setViewModel(this);
|
||||
getBinding().pb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
Aria.download(this).load(mTaskId).m3u8VodOption().jumPeerIndex(seekBar.getProgress());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void chooseUrl() {
|
||||
@@ -202,7 +219,7 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
|
||||
@Download.onTaskStart
|
||||
void taskStart(DownloadTask task) {
|
||||
if (task.getKey().equals(mUrl)) {
|
||||
getBinding().setFileSize(task.getConvertFileSize());
|
||||
getBinding().pb.setMax(task.getEntity().getM3U8Entity().getPeerNum());
|
||||
ALog.d(TAG, "isComplete = " + task.isComplete() + ", state = " + task.getState());
|
||||
}
|
||||
}
|
||||
@@ -364,7 +381,7 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
|
||||
|
||||
static class KeyUrlConverter implements IKeyUrlConverter{
|
||||
|
||||
@Override public String convert(String keyUrl) {
|
||||
@Override public String convert(String m3u8Url, String keyUrl) {
|
||||
ALog.d("TAG", "convertUrl....");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class FtpUploadActivity extends BaseActivity<ActivityFtpUploadBinding> {
|
||||
//upload();
|
||||
break;
|
||||
case R.id.cancel:
|
||||
Aria.upload(this).loadFtp(mTaskId).cancel();
|
||||
Aria.upload(this).loadFtp(mTaskId).cancel(false);
|
||||
mTaskId = -1;
|
||||
getBinding().setStateStr(getString(R.string.start));
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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.core.upload;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import com.arialyy.annotations.Upload;
|
||||
import com.arialyy.aria.core.Aria;
|
||||
import com.arialyy.aria.core.common.SFtpOption;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.task.UploadTask;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.frame.util.FileUtil;
|
||||
import com.arialyy.frame.util.show.T;
|
||||
import com.arialyy.simple.R;
|
||||
import com.arialyy.simple.base.BaseActivity;
|
||||
import com.arialyy.simple.common.ModifyUrlDialog;
|
||||
import com.arialyy.simple.databinding.ActivitySftpUploadBinding;
|
||||
import com.arialyy.simple.util.AppUtil;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/7/28. Ftp 文件上传demo
|
||||
*/
|
||||
public class SFtpUploadActivity extends BaseActivity<ActivitySftpUploadBinding> {
|
||||
private final int OPEN_FILE_MANAGER_CODE = 0xB1;
|
||||
private String mFilePath;
|
||||
private String mUrl;
|
||||
private UploadModule mModule;
|
||||
private long mTaskId = -1;
|
||||
private String user = "tester", pwd = "password";
|
||||
|
||||
@Override protected void init(Bundle savedInstanceState) {
|
||||
setTile("D_FTP 文件上传");
|
||||
super.init(savedInstanceState);
|
||||
Aria.upload(this).register();
|
||||
|
||||
getBinding().setViewModel(this);
|
||||
setUI();
|
||||
}
|
||||
|
||||
private void setUI() {
|
||||
mModule = ViewModelProviders.of(this).get(UploadModule.class);
|
||||
mModule.getSFtpInfo(this).observe(this, new Observer<UploadEntity>() {
|
||||
@Override public void onChanged(@Nullable UploadEntity entity) {
|
||||
if (entity != null) {
|
||||
mTaskId = entity.getId();
|
||||
if (entity.getFileSize() != 0) {
|
||||
getBinding().setFileSize(CommonUtil.formatFileSize(entity.getFileSize()));
|
||||
getBinding().setProgress(entity.isComplete() ? 100
|
||||
: (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
|
||||
}
|
||||
getBinding().setUrl(entity.getUrl());
|
||||
getBinding().setFilePath(entity.getFilePath());
|
||||
mUrl = entity.getUrl();
|
||||
mFilePath = entity.getFilePath();
|
||||
getBinding().setStateStr(getString(
|
||||
entity.getState() == IEntity.STATE_RUNNING ? R.string.stop : R.string.start));
|
||||
} else {
|
||||
getBinding().setStateStr(getString(R.string.resume));
|
||||
}
|
||||
}
|
||||
});
|
||||
setHelpCode();
|
||||
}
|
||||
|
||||
private void setHelpCode() {
|
||||
//try {
|
||||
// getBinding().codeView.setSource(AppUtil.getHelpCode(this, "FtpUpload.java"));
|
||||
//} catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
//}
|
||||
}
|
||||
|
||||
@Override protected int setLayoutId() {
|
||||
return R.layout.activity_sftp_upload;
|
||||
}
|
||||
|
||||
public void chooseUrl() {
|
||||
ModifyUrlDialog dialog =
|
||||
new ModifyUrlDialog(this, getString(R.string.modify_url_dialog_title), mUrl);
|
||||
dialog.show(getSupportFragmentManager(), "ModifyUrlDialog");
|
||||
}
|
||||
|
||||
public void chooseFilePath() {
|
||||
AppUtil.chooseFile(this, new File(mFilePath), null, OPEN_FILE_MANAGER_CODE);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.start:
|
||||
if (mTaskId == -1) {
|
||||
mTaskId = Aria.upload(this)
|
||||
.loadFtp(mFilePath)
|
||||
.setUploadUrl(mUrl)
|
||||
.sftpOption(getOption())
|
||||
.ignoreFilePathOccupy()
|
||||
.create();
|
||||
getBinding().setStateStr(getString(R.string.stop));
|
||||
break;
|
||||
}
|
||||
if (Aria.upload(this).loadFtp(mTaskId).isRunning()) {
|
||||
Aria.upload(this).loadFtp(mTaskId).stop();
|
||||
getBinding().setStateStr(getString(R.string.resume));
|
||||
} else {
|
||||
Aria.upload(this)
|
||||
.loadFtp(mTaskId)
|
||||
.sftpOption(getOption())
|
||||
.resume();
|
||||
getBinding().setStateStr(getString(R.string.stop));
|
||||
}
|
||||
//upload();
|
||||
break;
|
||||
case R.id.cancel:
|
||||
Aria.upload(this).loadFtp(mTaskId).cancel(false);
|
||||
mTaskId = -1;
|
||||
getBinding().setStateStr(getString(R.string.start));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void upload() {
|
||||
List<String> paths = new ArrayList<>();
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/1.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/2.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/3.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/4.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/5.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/6.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/7.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/8.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/9.jpg");
|
||||
paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/10.jpg");
|
||||
for (String path : paths) {
|
||||
Aria.upload(this)
|
||||
.loadFtp(path)
|
||||
.setUploadUrl(mUrl)
|
||||
.sftpOption(getOption())
|
||||
.ignoreFilePathOccupy()
|
||||
.create();
|
||||
}
|
||||
}
|
||||
|
||||
private SFtpOption getOption() {
|
||||
SFtpOption option = new SFtpOption();
|
||||
option.login(user, pwd);
|
||||
return option;
|
||||
}
|
||||
|
||||
@Upload.onWait void onWait(UploadTask task) {
|
||||
Log.d(TAG, task.getTaskName() + "_wait");
|
||||
}
|
||||
|
||||
@Upload.onPre public void onPre(UploadTask task) {
|
||||
getBinding().setFileSize(task.getConvertFileSize());
|
||||
}
|
||||
|
||||
@Upload.onTaskStart public void taskStart(UploadTask task) {
|
||||
Log.d(TAG, "开始上传,md5:" + FileUtil.getFileMD5(new File(task.getEntity().getFilePath())));
|
||||
}
|
||||
|
||||
@Upload.onTaskResume public void taskResume(UploadTask task) {
|
||||
Log.d(TAG, "恢复上传");
|
||||
}
|
||||
|
||||
@Upload.onTaskStop public void taskStop(UploadTask task) {
|
||||
getBinding().setSpeed("");
|
||||
Log.d(TAG, "停止上传");
|
||||
}
|
||||
|
||||
@Upload.onTaskCancel public void taskCancel(UploadTask task) {
|
||||
getBinding().setSpeed("");
|
||||
getBinding().setFileSize("");
|
||||
getBinding().setProgress(0);
|
||||
Log.d(TAG, "删除任务");
|
||||
}
|
||||
|
||||
@Upload.onTaskFail public void taskFail(UploadTask task) {
|
||||
Log.d(TAG, "上传失败");
|
||||
getBinding().setStateStr(getString(R.string.resume));
|
||||
}
|
||||
|
||||
@Upload.onTaskRunning public void taskRunning(UploadTask task) {
|
||||
Log.d(TAG, "PP = " + task.getPercent());
|
||||
getBinding().setProgress(task.getPercent());
|
||||
getBinding().setSpeed(task.getConvertSpeed());
|
||||
}
|
||||
|
||||
@Upload.onTaskComplete public void taskComplete(UploadTask task) {
|
||||
getBinding().setProgress(100);
|
||||
getBinding().setSpeed("");
|
||||
T.showShort(this, "文件:" + task.getEntity().getFileName() + ",上传完成");
|
||||
getBinding().setStateStr(getString(R.string.re_start));
|
||||
}
|
||||
|
||||
@Override protected void dataCallback(int result, Object data) {
|
||||
super.dataCallback(result, data);
|
||||
if (result == ModifyUrlDialog.MODIFY_URL_DIALOG_RESULT) {
|
||||
mModule.updateFtpUrl(this, String.valueOf(data));
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == OPEN_FILE_MANAGER_CODE && resultCode == RESULT_OK) {
|
||||
Uri uri = data.getData();
|
||||
if (uri != null) {
|
||||
mModule.updateFtpFilePath(this, uri.getPath());
|
||||
ALog.d(TAG, String.format("选择的文件路径:%s", uri.getPath()));
|
||||
} else {
|
||||
ALog.d(TAG, "没有选择文件");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,31 @@ public class UploadModule extends BaseViewModule {
|
||||
return liveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Ftp上传信息
|
||||
*/
|
||||
LiveData<UploadEntity> getSFtpInfo(Context context) {
|
||||
//String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.72:2121/aab/你好");
|
||||
//String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY,
|
||||
// Environment.getExternalStorageDirectory().getPath() + "/Download/AndroidAria.db");
|
||||
String url = "ftp://9.9.9.72:22/aab/你好";
|
||||
String filePath = "/mnt/sdcard/QQMusic-import-1.2.1.zip";
|
||||
|
||||
UploadEntity entity = Aria.upload(context).getFirstUploadEntity(filePath);
|
||||
if (entity != null) {
|
||||
uploadInfo = entity;
|
||||
AppUtil.setConfigValue(context, FTP_URL_KEY, uploadInfo.getUrl());
|
||||
AppUtil.setConfigValue(context, FTP_PATH_KEY, uploadInfo.getFilePath());
|
||||
} else {
|
||||
uploadInfo = new UploadEntity();
|
||||
uploadInfo.setUrl(url);
|
||||
uploadInfo.setFilePath(filePath);
|
||||
}
|
||||
|
||||
liveData.postValue(uploadInfo);
|
||||
return liveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Url
|
||||
*/
|
||||
|
||||
@@ -104,6 +104,7 @@ public class CommonModule extends BaseViewModule {
|
||||
R.drawable.ic_ftp,
|
||||
R.drawable.ic_ts,
|
||||
R.drawable.ic_live,
|
||||
R.drawable.ic_sftp,
|
||||
R.drawable.ic_sftp
|
||||
};
|
||||
int i = 0;
|
||||
|
||||
@@ -44,14 +44,14 @@
|
||||
tools:context=".core.download.SingleTaskActivity"
|
||||
>
|
||||
|
||||
<include layout="@layout/layout_bar"/>
|
||||
<include layout="@layout/layout_bar" />
|
||||
|
||||
<com.arialyy.simple.widget.SvgTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
bind:iconClickListener="@{() -> viewModel.chooseUrl()}"
|
||||
bind:svg_text_view_icon="@drawable/ic_modify"
|
||||
bind:text="@{@string/url(url)}"
|
||||
@@ -61,8 +61,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="16dp"
|
||||
bind:iconClickListener="@{() -> viewModel.chooseFilePath()}"
|
||||
bind:svg_text_view_icon="@drawable/ic_choose_file"
|
||||
bind:text="@{@string/file_path(filePath)}"
|
||||
@@ -76,13 +76,23 @@
|
||||
bind:stateStr="@{stateStr}"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSeekBar
|
||||
android:id="@+id/pb"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:background="@color/white"
|
||||
android:id="@+id/video_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginTop="16dp"
|
||||
/>
|
||||
android:background="@color/white"
|
||||
>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
83
app/src/main/res/layout/activity_sftp_upload.xml
Normal file
83
app/src/main/res/layout/activity_sftp_upload.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?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"
|
||||
/>
|
||||
<variable
|
||||
name="url"
|
||||
type="String"
|
||||
/>
|
||||
<variable
|
||||
name="filePath"
|
||||
type="String"
|
||||
/>
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.arialyy.simple.core.upload.SFtpUploadActivity"
|
||||
/>
|
||||
<variable
|
||||
name="stateStr"
|
||||
type="String"
|
||||
/>
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<include layout="@layout/layout_bar"/>
|
||||
|
||||
<com.arialyy.simple.widget.SvgTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
bind:iconClickListener="@{() -> viewModel.chooseUrl()}"
|
||||
bind:svg_text_view_icon="@drawable/ic_modify"
|
||||
bind:text="@{@string/url(url)}"
|
||||
/>
|
||||
|
||||
<com.arialyy.simple.widget.SvgTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
bind:iconClickListener="@{() -> viewModel.chooseFilePath()}"
|
||||
bind:svg_text_view_icon="@drawable/ic_choose_file"
|
||||
bind:text="@{@string/file_path(filePath)}"
|
||||
/>
|
||||
|
||||
<include
|
||||
layout="@layout/layout_content_single"
|
||||
bind:fileSize="@{fileSize}"
|
||||
bind:progress="@{progress}"
|
||||
bind:speed="@{speed}"
|
||||
bind:stateStr="@{stateStr}"
|
||||
/>
|
||||
|
||||
|
||||
<!-- <com.arialyy.simple.widget.CodeView-->
|
||||
<!-- android:id="@+id/code_view"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="match_parent"-->
|
||||
<!-- />-->
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
@@ -41,6 +41,7 @@
|
||||
<item>M3U8 点播文件下载</item>
|
||||
<item>M3U8 直播文件下载</item>
|
||||
<item>SFTP 下载</item>
|
||||
<item>SFTP 上传</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="main_items_desc">
|
||||
@@ -53,6 +54,7 @@
|
||||
<item>下载M3U8点播文件</item>
|
||||
<item>下载M3U8直播文件</item>
|
||||
<item>SFTP单文件下载</item>
|
||||
<item>SFTP单文件上传</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="download_items">
|
||||
|
||||
@@ -44,8 +44,8 @@ task clean(type: Delete) {
|
||||
}
|
||||
|
||||
ext {
|
||||
versionCode = 383
|
||||
versionName = '3.8.3'
|
||||
versionCode = 385
|
||||
versionName = '3.8.5'
|
||||
userOrg = 'arialyy'
|
||||
groupId = 'com.arialyy.aria'
|
||||
publishVersion = versionName
|
||||
|
||||
Reference in New Issue
Block a user