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