parent
50b265f22a
commit
d217ebcb25
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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;
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -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 |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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, "没有选择文件"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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> |
Loading…
Reference in new issue