源代码备份
This commit is contained in:
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.net.TrafficStats;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import aria.apache.commons.net.ftp.FTP;
|
||||
import aria.apache.commons.net.ftp.FTPClient;
|
||||
import aria.apache.commons.net.ftp.FTPClientConfig;
|
||||
import aria.apache.commons.net.ftp.FTPFile;
|
||||
import aria.apache.commons.net.ftp.FTPReply;
|
||||
import aria.apache.commons.net.ftp.FTPSClient;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.common.FtpConnectionMode;
|
||||
import com.arialyy.aria.core.loader.IInfoTask;
|
||||
import com.arialyy.aria.core.loader.ILoaderVisitor;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaFTPException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CheckUtil;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.SSLContextUtil;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.UUID;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/25. 获取ftp文件夹信息
|
||||
*/
|
||||
public abstract class AbsFtpInfoTask<ENTITY extends AbsEntity, TASK_WRAPPER extends AbsTaskWrapper<ENTITY>>
|
||||
implements Runnable, IInfoTask {
|
||||
|
||||
protected final String TAG = CommonUtil.getClassName(getClass());
|
||||
protected ENTITY mEntity;
|
||||
protected TASK_WRAPPER mTaskWrapper;
|
||||
protected FtpTaskOption mTaskOption;
|
||||
private int mConnectTimeOut;
|
||||
protected long mSize = 0;
|
||||
protected String charSet = "UTF-8";
|
||||
private Callback callback;
|
||||
private boolean isStop = false, isCancel = false;
|
||||
|
||||
public AbsFtpInfoTask(TASK_WRAPPER taskWrapper) {
|
||||
mTaskWrapper = taskWrapper;
|
||||
mEntity = taskWrapper.getEntity();
|
||||
mTaskOption = (FtpTaskOption) taskWrapper.getTaskOption();
|
||||
mConnectTimeOut = AriaConfig.getInstance().getDConfig().getConnectTimeOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的远程文件路径
|
||||
*
|
||||
* @return 远程文件路径
|
||||
*/
|
||||
protected abstract String getRemotePath();
|
||||
|
||||
/**
|
||||
* 处理ftp列表信息
|
||||
*
|
||||
* @param client ftp 客户端对象
|
||||
* @param files remotePath 对应的文件列表
|
||||
* @param convertedRemotePath 已转换的可被服务器识别的remotePath
|
||||
*/
|
||||
protected abstract void handelFileInfo(FTPClient client, FTPFile[] files,
|
||||
String convertedRemotePath) throws IOException;
|
||||
|
||||
@Override public void stop() {
|
||||
isStop = true;
|
||||
}
|
||||
|
||||
@Override public void cancel() {
|
||||
isCancel = true;
|
||||
}
|
||||
|
||||
@Override public void setCallback(Callback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
TrafficStats.setThreadStatsTag(UUID.randomUUID().toString().hashCode());
|
||||
FTPClient client = null;
|
||||
try {
|
||||
client = createFtpClient();
|
||||
if (client == null) {
|
||||
ALog.e(TAG, String.format("任务【%s】失败", mTaskOption.getUrlEntity().url));
|
||||
return;
|
||||
}
|
||||
String convertedRemotePath = CommonUtil.convertFtpChar(charSet, getRemotePath());
|
||||
FTPFile[] files = client.listFiles(convertedRemotePath);
|
||||
handelFileInfo(client, files, convertedRemotePath);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
handleFail(client, "FTP错误信息", e, true);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
handleFail(client, "FTP错误信息", e, true);
|
||||
} finally {
|
||||
closeClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理拦截
|
||||
*
|
||||
* @param ftpFiles remotePath路径下的所有文件
|
||||
* @return {@code false} 拦截器处理完成任务,任务将不再执行,{@code true} 拦截器处理任务完成任务,任务继续执行
|
||||
*/
|
||||
protected boolean onInterceptor(FTPClient client, FTPFile[] ftpFiles) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void onPreComplete(int code) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建FTP客户端
|
||||
*/
|
||||
private FTPClient createFtpClient() throws IOException, InterruptedException {
|
||||
FTPClient client = null;
|
||||
final FtpUrlEntity urlEntity = mTaskOption.getUrlEntity();
|
||||
if (CheckUtil.checkIp(urlEntity.hostName)) {
|
||||
client = newInstanceClient(urlEntity);
|
||||
client.setConnectTimeout(mConnectTimeOut); // 连接10s超时
|
||||
InetAddress ip = InetAddress.getByName(urlEntity.hostName);
|
||||
|
||||
client = connect(client, new InetAddress[] { ip }, 0, Integer.parseInt(urlEntity.port));
|
||||
mTaskOption.getUrlEntity().validAddr = ip;
|
||||
} else {
|
||||
DNSQueryThread dnsThread = new DNSQueryThread(urlEntity.hostName);
|
||||
dnsThread.start();
|
||||
dnsThread.join(mConnectTimeOut);
|
||||
InetAddress[] ips = dnsThread.getIps();
|
||||
client = connect(newInstanceClient(urlEntity), ips, 0, Integer.parseInt(urlEntity.port));
|
||||
}
|
||||
|
||||
if (client == null) {
|
||||
handleFail(client, String.format("链接失败, url: %s", mTaskOption.getUrlEntity().url), null,
|
||||
true);
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean loginSuccess = true;
|
||||
if (urlEntity.needLogin) {
|
||||
try {
|
||||
if (TextUtils.isEmpty(urlEntity.account)) {
|
||||
loginSuccess = client.login(urlEntity.user, urlEntity.password);
|
||||
} else {
|
||||
loginSuccess = client.login(urlEntity.user, urlEntity.password, urlEntity.account);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
ALog.e(TAG, String.format("登录失败,错误码为:%s, msg:%s", client.getReplyCode(),
|
||||
client.getReplyString()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loginSuccess) {
|
||||
handleFail(client, "登录失败", null, false);
|
||||
client.disconnect();
|
||||
return null;
|
||||
}
|
||||
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
handleFail(client, String.format("无法连接到ftp服务器,filePath: %s, url: %s", mEntity.getKey(),
|
||||
mTaskOption.getUrlEntity().url), null, true);
|
||||
client.disconnect();
|
||||
return null;
|
||||
}
|
||||
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
|
||||
charSet = "UTF-8";
|
||||
reply = client.sendCommand("OPTS UTF8", "ON");
|
||||
if (reply != FTPReply.COMMAND_IS_SUPERFLUOUS) {
|
||||
ALog.i(TAG, "D_FTP 服务器不支持开启UTF8编码,尝试使用Aria手动设置的编码");
|
||||
if (!TextUtils.isEmpty(mTaskOption.getCharSet())) {
|
||||
charSet = mTaskOption.getCharSet();
|
||||
}
|
||||
}
|
||||
client.setControlEncoding(charSet);
|
||||
client.setDataTimeout(10 * 1000);
|
||||
if (mTaskOption.getConnMode() == FtpConnectionMode.DATA_CONNECTION_MODE_ACTIVITY) {
|
||||
client.enterLocalActiveMode();
|
||||
if (mTaskOption.getMinPort() != 0 && mTaskOption.getMaxPort() != 0) {
|
||||
client.setActivePortRange(mTaskOption.getMinPort(), mTaskOption.getMaxPort());
|
||||
}
|
||||
if (!TextUtils.isEmpty(mTaskOption.getActiveExternalIPAddress())) {
|
||||
client.setActiveExternalIPAddress(mTaskOption.getActiveExternalIPAddress());
|
||||
}
|
||||
} else {
|
||||
client.enterLocalPassiveMode();
|
||||
}
|
||||
client.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
protected void closeClient(FTPClient client) {
|
||||
try {
|
||||
if (client != null && client.isConnected()) {
|
||||
client.logout();
|
||||
client.disconnect();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建FTP/FTPS客户端
|
||||
*/
|
||||
private FTPClient newInstanceClient(FtpUrlEntity urlEntity) {
|
||||
FTPClient temp;
|
||||
if (urlEntity.isFtps) {
|
||||
FTPSClient sClient;
|
||||
SSLContext sslContext = SSLContextUtil.getSSLContext(
|
||||
urlEntity.idEntity.keyAlias, urlEntity.idEntity.storePath, urlEntity.protocol);
|
||||
if (sslContext == null) {
|
||||
sClient = new FTPSClient(urlEntity.protocol, urlEntity.isImplicit);
|
||||
} else {
|
||||
sClient = new FTPSClient(true, sslContext);
|
||||
}
|
||||
|
||||
temp = sClient;
|
||||
} else {
|
||||
temp = new FTPClient();
|
||||
}
|
||||
|
||||
FTPClientConfig clientConfig;
|
||||
if (mTaskOption.getClientConfig() != null) {
|
||||
clientConfig = mTaskOption.getClientConfig();
|
||||
} else {
|
||||
clientConfig = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
|
||||
clientConfig.setServerLanguageCode("en");
|
||||
}
|
||||
temp.configure(clientConfig);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接到ftp服务器
|
||||
*/
|
||||
private FTPClient connect(FTPClient client, InetAddress[] ips, int index, int port) {
|
||||
if (ips == null || ips.length == 0) {
|
||||
ALog.w(TAG, "无可用ip");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
client.setConnectTimeout(mConnectTimeOut); //需要先设置超时,这样才不会出现阻塞
|
||||
client.connect(ips[index], port);
|
||||
mTaskOption.getUrlEntity().validAddr = ips[index];
|
||||
|
||||
FtpUrlEntity urlEntity = mTaskOption.getUrlEntity();
|
||||
if (urlEntity.isFtps) {
|
||||
FTPSClient sClient = (FTPSClient) client;
|
||||
sClient.execPBSZ(0);
|
||||
sClient.execPROT("P");
|
||||
}
|
||||
|
||||
return client;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
closeClient(client);
|
||||
if (index + 1 >= ips.length) {
|
||||
ALog.w(TAG, "遇到[ECONNREFUSED-连接被服务器拒绝]错误,已没有其他地址,链接失败;如果是ftps,请检查端口是否使用了ftp的端口而不是ftps的端口");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
ALog.w(TAG, "遇到[ECONNREFUSED-连接被服务器拒绝]错误,正在尝试下一个地址");
|
||||
return connect(newInstanceClient(mTaskOption.getUrlEntity()), ips, index + 1, port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历FTP服务器上对应文件或文件夹大小
|
||||
*
|
||||
* @throws IOException 字符串编码转换错误
|
||||
*/
|
||||
protected long getFileSize(FTPFile[] files, FTPClient client, String dirName) throws IOException {
|
||||
long size = 0;
|
||||
String path = dirName + "/";
|
||||
for (FTPFile file : files) {
|
||||
if (file.isFile()) {
|
||||
size += file.getSize();
|
||||
handleFile(client, path + file.getName(), file);
|
||||
} else {
|
||||
String remotePath = CommonUtil.convertFtpChar(charSet, path + file.getName());
|
||||
size += getFileSize(client.listFiles(remotePath), client, path + file.getName());
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理FTP文件信息
|
||||
*
|
||||
* @param remotePath ftp服务器文件夹路径
|
||||
* @param ftpFile ftp服务器上对应的文件
|
||||
*/
|
||||
protected void handleFile(FTPClient client, String remotePath, FTPFile ftpFile) {
|
||||
}
|
||||
|
||||
protected void handleFail(FTPClient client, String msg, Exception e, boolean needRetry) {
|
||||
if (isStop || isCancel) {
|
||||
return;
|
||||
}
|
||||
if (callback != null) {
|
||||
if (client == null) {
|
||||
msg = "创建ftp客户端失败";
|
||||
needRetry = false;
|
||||
} else {
|
||||
msg = String.format("%s, code: %s, msg: %s", msg, client.getReplyCode(),
|
||||
client.getReplyString());
|
||||
needRetry = needRetry && !CheckUtil.ftpIsBadRequest(client.getReplyCode());
|
||||
}
|
||||
|
||||
callback.onFail(mEntity, new AriaFTPException(msg), needRetry);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSucceed(CompleteInfo info){
|
||||
callback.onSucceed(mEntity.getKey(), info);
|
||||
}
|
||||
|
||||
@Override public void accept(ILoaderVisitor visitor) {
|
||||
visitor.addComponent(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用IP的超时线程,InetAddress.getByName没有超时功能,需要自己处理超时
|
||||
*/
|
||||
private static class DNSQueryThread extends Thread {
|
||||
|
||||
private String hostName;
|
||||
private InetAddress[] ips;
|
||||
|
||||
DNSQueryThread(String hostName) {
|
||||
this.hostName = hostName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ips = InetAddress.getAllByName(hostName);
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized InetAddress[] getIps() {
|
||||
return ips;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import aria.apache.commons.net.ftp.FTP;
|
||||
import aria.apache.commons.net.ftp.FTPClient;
|
||||
import aria.apache.commons.net.ftp.FTPClientConfig;
|
||||
import aria.apache.commons.net.ftp.FTPReply;
|
||||
import aria.apache.commons.net.ftp.FTPSClient;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import com.arialyy.aria.core.common.FtpConnectionMode;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
|
||||
import com.arialyy.aria.exception.AriaFTPException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.SSLContextUtil;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-18
|
||||
*/
|
||||
public abstract class BaseFtpThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
|
||||
protected FtpTaskOption mTaskOption;
|
||||
protected String charSet;
|
||||
|
||||
protected BaseFtpThreadTaskAdapter(SubThreadConfig config) {
|
||||
super(config);
|
||||
mTaskOption = (FtpTaskOption) getTaskWrapper().getTaskOption();
|
||||
}
|
||||
|
||||
protected void closeClient(FTPClient client) {
|
||||
try {
|
||||
if (client != null && client.isConnected()) {
|
||||
client.logout();
|
||||
client.disconnect();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建FTP客户端
|
||||
*/
|
||||
protected FTPClient createClient() {
|
||||
FTPClient client = null;
|
||||
final FtpUrlEntity urlEntity = mTaskOption.getUrlEntity();
|
||||
if (urlEntity.validAddr == null) {
|
||||
try {
|
||||
InetAddress[] ips = InetAddress.getAllByName(urlEntity.hostName);
|
||||
client = connect(newInstanceClient(urlEntity), ips, 0, Integer.parseInt(urlEntity.port));
|
||||
if (client == null) {
|
||||
return null;
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
client = newInstanceClient(urlEntity);
|
||||
try {
|
||||
client.connect(urlEntity.validAddr, Integer.parseInt(urlEntity.port));
|
||||
} catch (java.io.IOException e) {
|
||||
ALog.e(TAG, ALog.getExceptionString(e));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (client == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (urlEntity.isFtps) {
|
||||
FTPSClient sClient = (FTPSClient) client;
|
||||
sClient.execPBSZ(0);
|
||||
sClient.execPROT("P");
|
||||
}
|
||||
|
||||
if (urlEntity.needLogin) {
|
||||
if (TextUtils.isEmpty(urlEntity.account)) {
|
||||
client.login(urlEntity.user, urlEntity.password);
|
||||
} else {
|
||||
client.login(urlEntity.user, urlEntity.password, urlEntity.account);
|
||||
}
|
||||
}
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
client.disconnect();
|
||||
fail(new AriaFTPException(
|
||||
String.format("无法连接到ftp服务器,错误码为:%s,msg:%s", reply, client.getReplyString())), false);
|
||||
return null;
|
||||
}
|
||||
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码
|
||||
charSet = "UTF-8";
|
||||
if (reply != FTPReply.COMMAND_IS_SUPERFLUOUS) {
|
||||
if (!TextUtils.isEmpty(mTaskOption.getCharSet())) {
|
||||
charSet = mTaskOption.getCharSet();
|
||||
}
|
||||
}
|
||||
client.setControlEncoding(charSet);
|
||||
//client.setDataTimeout(getTaskConfig().getIOTimeOut());
|
||||
client.setDataTimeout(1000);
|
||||
client.setConnectTimeout(getTaskConfig().getConnectTimeOut());
|
||||
if (mTaskOption.getConnMode() == FtpConnectionMode.DATA_CONNECTION_MODE_ACTIVITY) {
|
||||
client.enterLocalActiveMode();
|
||||
if (mTaskOption.getMinPort() != 0 && mTaskOption.getMaxPort() != 0) {
|
||||
client.setActivePortRange(mTaskOption.getMinPort(), mTaskOption.getMaxPort());
|
||||
}
|
||||
if (!TextUtils.isEmpty(mTaskOption.getActiveExternalIPAddress())) {
|
||||
client.setActiveExternalIPAddress(mTaskOption.getActiveExternalIPAddress());
|
||||
}
|
||||
} else {
|
||||
client.enterLocalPassiveMode();
|
||||
}
|
||||
client.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
client.setControlKeepAliveTimeout(5000);
|
||||
} catch (IOException e) {
|
||||
closeClient(client);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建FTP/FTPS客户端
|
||||
*/
|
||||
private FTPClient newInstanceClient(FtpUrlEntity urlEntity) {
|
||||
FTPClient temp;
|
||||
if (urlEntity.isFtps) {
|
||||
FTPSClient sClient;
|
||||
SSLContext sslContext = SSLContextUtil.getSSLContext(
|
||||
urlEntity.idEntity.keyAlias, urlEntity.idEntity.storePath, urlEntity.protocol);
|
||||
if (sslContext == null) {
|
||||
sClient = new FTPSClient(urlEntity.protocol, urlEntity.isImplicit);
|
||||
} else {
|
||||
sClient = new FTPSClient(true, sslContext);
|
||||
}
|
||||
|
||||
temp = sClient;
|
||||
} else {
|
||||
temp = new FTPClient();
|
||||
}
|
||||
|
||||
FTPClientConfig clientConfig;
|
||||
if (mTaskOption.getClientConfig() != null) {
|
||||
clientConfig = mTaskOption.getClientConfig();
|
||||
} else {
|
||||
clientConfig = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
|
||||
clientConfig.setServerLanguageCode("en");
|
||||
}
|
||||
temp.configure(clientConfig);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接到ftp服务器
|
||||
*/
|
||||
private FTPClient connect(FTPClient client, InetAddress[] ips, int index, int port) {
|
||||
try {
|
||||
client.connect(ips[index], port);
|
||||
mTaskOption.getUrlEntity().validAddr = ips[index];
|
||||
return client;
|
||||
} catch (java.io.IOException e) {
|
||||
try {
|
||||
if (client.isConnected()) {
|
||||
client.disconnect();
|
||||
}
|
||||
} catch (java.io.IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
if (index + 1 >= ips.length) {
|
||||
ALog.w(TAG, "遇到[ECONNREFUSED-连接被服务器拒绝]错误,已没有其他地址,链接失败");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
ALog.w(TAG, "遇到[ECONNREFUSED-连接被服务器拒绝]错误,正在尝试下一个地址");
|
||||
return connect(new FTPClient(), ips, index + 1, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import aria.apache.commons.net.ftp.FTPClientConfig;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import com.arialyy.aria.core.common.FtpConnectionMode;
|
||||
import com.arialyy.aria.core.inf.ITaskOption;
|
||||
import com.arialyy.aria.core.processor.FtpInterceptHandler;
|
||||
import com.arialyy.aria.core.processor.IFtpUploadInterceptor;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.net.Proxy;
|
||||
|
||||
/**
|
||||
* fTP任务设置的信息,如:用户名、密码、端口等信息
|
||||
*/
|
||||
public final class FtpTaskOption implements ITaskOption {
|
||||
|
||||
/**
|
||||
* 账号和密码
|
||||
*/
|
||||
private FtpUrlEntity urlEntity;
|
||||
|
||||
private Proxy proxy;
|
||||
|
||||
/**
|
||||
* 字符编码,默认为"utf-8"
|
||||
*/
|
||||
private String charSet = "utf-8";
|
||||
|
||||
/**
|
||||
* 上传拦截器
|
||||
*/
|
||||
private SoftReference<IFtpUploadInterceptor> uploadInterceptor;
|
||||
|
||||
/**
|
||||
* 上传到服务器文件的新文件名{@link FtpInterceptHandler#getNewFileName()}
|
||||
*/
|
||||
private String newFileName;
|
||||
|
||||
/**
|
||||
* client配置信息
|
||||
*/
|
||||
private FTPClientConfig clientConfig;
|
||||
|
||||
/**
|
||||
* 连接模式,默认为被动模式
|
||||
* {@link FtpConnectionMode}
|
||||
*/
|
||||
private int connMode;
|
||||
|
||||
/**
|
||||
* 主动模式下的端口范围
|
||||
*/
|
||||
private int minPort, maxPort;
|
||||
|
||||
/**
|
||||
* 主动模式下,对外ip(可被Ftp服务器访问的ip)
|
||||
*/
|
||||
private String activeExternalIPAddress;
|
||||
|
||||
//---------------- ftp client 配置信息 start
|
||||
private String defaultDateFormatStr = null;
|
||||
private String recentDateFormatStr = null;
|
||||
private String serverLanguageCode = null;
|
||||
private String shortMonthNames = null;
|
||||
private String serverTimeZoneId = null;
|
||||
private String systemKey = FTPClientConfig.SYST_UNIX;
|
||||
//---------------- ftp client 配置信息 end
|
||||
|
||||
public String getActiveExternalIPAddress() {
|
||||
return activeExternalIPAddress;
|
||||
}
|
||||
|
||||
public void setActiveExternalIPAddress(String activeExternalIPAddress) {
|
||||
this.activeExternalIPAddress = activeExternalIPAddress;
|
||||
}
|
||||
|
||||
public int getMinPort() {
|
||||
return minPort;
|
||||
}
|
||||
|
||||
public void setMinPort(int minPort) {
|
||||
this.minPort = minPort;
|
||||
}
|
||||
|
||||
public int getMaxPort() {
|
||||
return maxPort;
|
||||
}
|
||||
|
||||
public void setMaxPort(int maxPort) {
|
||||
this.maxPort = maxPort;
|
||||
}
|
||||
|
||||
public int getConnMode() {
|
||||
return connMode;
|
||||
}
|
||||
|
||||
public void setConnMode(int connMode) {
|
||||
this.connMode = connMode;
|
||||
}
|
||||
|
||||
public FTPClientConfig getClientConfig() {
|
||||
if (clientConfig == null) {
|
||||
clientConfig = new FTPClientConfig(systemKey);
|
||||
if (defaultDateFormatStr != null) {
|
||||
clientConfig.setDefaultDateFormatStr(defaultDateFormatStr);
|
||||
}
|
||||
if (recentDateFormatStr != null) {
|
||||
clientConfig.setRecentDateFormatStr(recentDateFormatStr);
|
||||
}
|
||||
if (serverLanguageCode != null) {
|
||||
clientConfig.setServerLanguageCode(serverLanguageCode);
|
||||
}
|
||||
if (shortMonthNames != null) {
|
||||
clientConfig.setShortMonthNames(shortMonthNames);
|
||||
}
|
||||
if (serverTimeZoneId != null) {
|
||||
clientConfig.setServerTimeZoneId(serverTimeZoneId);
|
||||
}
|
||||
}
|
||||
return clientConfig;
|
||||
}
|
||||
|
||||
public void setClientConfig(FTPClientConfig clientConfig) {
|
||||
this.clientConfig = clientConfig;
|
||||
}
|
||||
|
||||
public String getNewFileName() {
|
||||
return newFileName;
|
||||
}
|
||||
|
||||
public void setNewFileName(String newFileName) {
|
||||
this.newFileName = newFileName;
|
||||
}
|
||||
|
||||
public IFtpUploadInterceptor getUploadInterceptor() {
|
||||
return uploadInterceptor == null ? null : uploadInterceptor.get();
|
||||
}
|
||||
|
||||
public void setUploadInterceptor(IFtpUploadInterceptor uploadInterceptor) {
|
||||
this.uploadInterceptor = new SoftReference<>(uploadInterceptor);
|
||||
}
|
||||
|
||||
public FtpUrlEntity getUrlEntity() {
|
||||
return urlEntity;
|
||||
}
|
||||
|
||||
public void setUrlEntity(FtpUrlEntity urlEntity) {
|
||||
this.urlEntity = urlEntity;
|
||||
}
|
||||
|
||||
public void setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
public Proxy getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
public String getCharSet() {
|
||||
return TextUtils.isEmpty(charSet) ? "UTF-8" : charSet;
|
||||
}
|
||||
|
||||
public void setCharSet(String charSet) {
|
||||
this.charSet = charSet;
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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 aria.apache.commons.net.ftp.FTPClient;
|
||||
import aria.apache.commons.net.ftp.FTPFile;
|
||||
import aria.apache.commons.net.ftp.FTPReply;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.exception.AriaFTPException;
|
||||
import com.arialyy.aria.ftp.AbsFtpInfoTask;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/25.
|
||||
* 获取ftp文件信息
|
||||
*/
|
||||
final class FtpDFileInfoTask extends AbsFtpInfoTask<DownloadEntity, DTaskWrapper> {
|
||||
|
||||
FtpDFileInfoTask(DTaskWrapper taskEntity) {
|
||||
super(taskEntity);
|
||||
}
|
||||
|
||||
@Override protected void handleFile(FTPClient client, String remotePath, FTPFile ftpFile) {
|
||||
super.handleFile(client, remotePath, ftpFile);
|
||||
if (!FileUtil.checkMemorySpace(mEntity.getFilePath(), ftpFile.getSize())) {
|
||||
handleFail(client, "内存空间不足", new AriaFTPException(
|
||||
String.format("获取ftp文件信息失败,内存空间不足, filePath: %s", mEntity.getFilePath())),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handelFileInfo(FTPClient client, FTPFile[] files, String convertedRemotePath)
|
||||
throws IOException {
|
||||
boolean isExist = files.length != 0;
|
||||
if (!isExist) {
|
||||
int i = convertedRemotePath.lastIndexOf(File.separator);
|
||||
FTPFile[] files1;
|
||||
if (i == -1) {
|
||||
files1 = client.listFiles();
|
||||
} else {
|
||||
files1 = client.listFiles(convertedRemotePath.substring(0, i + 1));
|
||||
}
|
||||
if (files1.length > 0) {
|
||||
ALog.i(TAG,
|
||||
String.format("路径【%s】下的文件列表 ===================================", getRemotePath()));
|
||||
for (FTPFile file : files1) {
|
||||
ALog.d(TAG, file.toString());
|
||||
}
|
||||
ALog.i(TAG,
|
||||
"================================= --end-- ===================================");
|
||||
} else {
|
||||
ALog.w(TAG, String.format("获取文件列表失败,msg:%s", client.getReplyString()));
|
||||
}
|
||||
closeClient(client);
|
||||
|
||||
handleFail(client,
|
||||
String.format("文件不存在,url: %s, remotePath:%s", mTaskOption.getUrlEntity().url,
|
||||
getRemotePath()), null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理拦截功能
|
||||
if (!onInterceptor(client, files)) {
|
||||
closeClient(client);
|
||||
ALog.d(TAG, "拦截器处理完成任务");
|
||||
return;
|
||||
}
|
||||
|
||||
//为了防止编码错乱,需要使用原始字符串
|
||||
mSize = getFileSize(files, client, getRemotePath());
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
closeClient(client);
|
||||
handleFail(client, "获取文件信息错误,url: " + mTaskOption.getUrlEntity().url, null, true);
|
||||
return;
|
||||
}
|
||||
mTaskWrapper.setCode(reply);
|
||||
if (mSize != 0) {
|
||||
mEntity.setFileSize(mSize);
|
||||
}
|
||||
onPreComplete(reply);
|
||||
mEntity.update();
|
||||
}
|
||||
|
||||
@Override protected String getRemotePath() {
|
||||
return mTaskOption.getUrlEntity().remotePath;
|
||||
}
|
||||
|
||||
@Override protected void onPreComplete(int code) {
|
||||
ALog.i(TAG, "FTP下载预处理完成");
|
||||
super.onPreComplete(code);
|
||||
if (mSize != mTaskWrapper.getEntity().getFileSize()) {
|
||||
mTaskWrapper.setNewTask(true);
|
||||
}
|
||||
mEntity.setFileSize(mSize);
|
||||
onSucceed(new CompleteInfo(code, mTaskWrapper));
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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 android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import aria.apache.commons.net.ftp.FTPClient;
|
||||
import aria.apache.commons.net.ftp.FTPFile;
|
||||
import aria.apache.commons.net.ftp.FTPReply;
|
||||
import com.arialyy.aria.core.FtpUrlEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.download.DGTaskWrapper;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.ftp.AbsFtpInfoTask;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
import com.arialyy.aria.orm.DbEntity;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.DeleteDGRecord;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/25. 获取ftp文件夹信息
|
||||
*/
|
||||
final class FtpDGInfoTask extends AbsFtpInfoTask<DownloadGroupEntity, DGTaskWrapper> {
|
||||
|
||||
FtpDGInfoTask(DGTaskWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
if (mTaskWrapper.getEntity().getFileSize() > 1 && checkSubOption()) {
|
||||
onSucceed(new CompleteInfo(200, mTaskWrapper));
|
||||
} else {
|
||||
super.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handelFileInfo(FTPClient client, FTPFile[] files, String convertedRemotePath)
|
||||
throws IOException {
|
||||
boolean isExist = files.length != 0;
|
||||
if (!isExist) {
|
||||
int i = convertedRemotePath.lastIndexOf(File.separator);
|
||||
FTPFile[] files1;
|
||||
if (i == -1) {
|
||||
files1 = client.listFiles();
|
||||
} else {
|
||||
files1 = client.listFiles(convertedRemotePath.substring(0, i + 1));
|
||||
}
|
||||
if (files1.length > 0) {
|
||||
ALog.i(TAG,
|
||||
String.format("路径【%s】下的文件列表 ===================================", getRemotePath()));
|
||||
for (FTPFile file : files1) {
|
||||
ALog.d(TAG, file.toString());
|
||||
}
|
||||
ALog.i(TAG,
|
||||
"================================= --end-- ===================================");
|
||||
} else {
|
||||
ALog.w(TAG, String.format("获取文件列表失败,msg:%s", client.getReplyString()));
|
||||
}
|
||||
closeClient(client);
|
||||
|
||||
handleFail(client,
|
||||
String.format("文件不存在,url: %s, remotePath:%s", mTaskOption.getUrlEntity().url,
|
||||
getRemotePath()), null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理拦截功能
|
||||
if (!onInterceptor(client, files)) {
|
||||
closeClient(client);
|
||||
ALog.d(TAG, "拦截器处理完成任务");
|
||||
return;
|
||||
}
|
||||
|
||||
//为了防止编码错乱,需要使用原始字符串
|
||||
mSize = getFileSize(files, client, getRemotePath());
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
closeClient(client);
|
||||
handleFail(client, "获取文件信息错误,url: " + mTaskOption.getUrlEntity().url, null, true);
|
||||
return;
|
||||
}
|
||||
mTaskWrapper.setCode(reply);
|
||||
if (mSize != 0) {
|
||||
mEntity.setFileSize(mSize);
|
||||
}
|
||||
onPreComplete(reply);
|
||||
mEntity.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查子任务的任务设置
|
||||
*
|
||||
* @return true 子任务任务设置成功,false 子任务任务设置失败
|
||||
*/
|
||||
private boolean checkSubOption() {
|
||||
for (DTaskWrapper wrapper : mTaskWrapper.getSubTaskWrapper()) {
|
||||
if (wrapper.getTaskOption() == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override protected String getRemotePath() {
|
||||
return mTaskOption.getUrlEntity().remotePath;
|
||||
}
|
||||
|
||||
@Override protected void handleFile(FTPClient client, String remotePath, FTPFile ftpFile) {
|
||||
super.handleFile(client, remotePath, ftpFile);
|
||||
addEntity(remotePath, ftpFile);
|
||||
}
|
||||
|
||||
@Override protected void onPreComplete(int code) {
|
||||
super.onPreComplete(code);
|
||||
mEntity.setFileSize(mSize);
|
||||
for (DTaskWrapper wrapper : mTaskWrapper.getSubTaskWrapper()) {
|
||||
cloneInfo(wrapper);
|
||||
}
|
||||
onSucceed(new CompleteInfo(code, mTaskWrapper));
|
||||
}
|
||||
|
||||
private void cloneInfo(DTaskWrapper subWrapper) {
|
||||
FtpTaskOption option = (FtpTaskOption) mTaskWrapper.getTaskOption();
|
||||
FtpUrlEntity urlEntity = option.getUrlEntity().clone();
|
||||
Uri uri = Uri.parse(subWrapper.getEntity().getUrl());
|
||||
String remotePath = uri.getPath();
|
||||
urlEntity.remotePath = TextUtils.isEmpty(remotePath) ? "/" : remotePath;
|
||||
|
||||
FtpTaskOption subOption = new FtpTaskOption();
|
||||
subOption.setUrlEntity(urlEntity);
|
||||
subOption.setCharSet(option.getCharSet());
|
||||
subOption.setProxy(option.getProxy());
|
||||
subOption.setClientConfig(option.getClientConfig());
|
||||
subOption.setNewFileName(option.getNewFileName());
|
||||
subOption.setProxy(option.getProxy());
|
||||
subOption.setUploadInterceptor(option.getUploadInterceptor());
|
||||
|
||||
subWrapper.setTaskOption(subOption);
|
||||
}
|
||||
|
||||
/**
|
||||
* FTP文件夹的子任务实体 在这生成
|
||||
*/
|
||||
private void addEntity(String remotePath, FTPFile ftpFile) {
|
||||
final FtpUrlEntity urlEntity = mTaskOption.getUrlEntity().clone();
|
||||
String url =
|
||||
urlEntity.scheme + "://" + urlEntity.hostName + ":" + urlEntity.port + "/" + remotePath;
|
||||
if (checkEntityExist(url)) {
|
||||
ALog.w(TAG, "子任务已存在,取消子任务的添加,url = " + url);
|
||||
return;
|
||||
}
|
||||
DownloadEntity entity = new DownloadEntity();
|
||||
entity.setUrl(url);
|
||||
entity.setFilePath(mEntity.getDirPath() + "/" + remotePath);
|
||||
int lastIndex = remotePath.lastIndexOf("/");
|
||||
String fileName = lastIndex < 0 ? CommonUtil.keyToHashKey(remotePath)
|
||||
: remotePath.substring(lastIndex + 1);
|
||||
entity.setFileName(
|
||||
new String(fileName.getBytes(), Charset.forName(mTaskOption.getCharSet())));
|
||||
entity.setGroupHash(mEntity.getGroupHash());
|
||||
entity.setGroupChild(true);
|
||||
entity.setConvertFileSize(CommonUtil.formatFileSize(ftpFile.getSize()));
|
||||
entity.setFileSize(ftpFile.getSize());
|
||||
|
||||
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", entity.getFilePath())) {
|
||||
DbEntity.deleteData(DownloadEntity.class, "downloadPath=?", entity.getFilePath());
|
||||
}
|
||||
entity.insert();
|
||||
|
||||
DTaskWrapper subWrapper = new DTaskWrapper(entity);
|
||||
subWrapper.setGroupTask(true);
|
||||
subWrapper.setGroupHash(mEntity.getGroupHash());
|
||||
subWrapper.setRequestType(AbsTaskWrapper.D_FTP);
|
||||
urlEntity.url = entity.getUrl();
|
||||
urlEntity.remotePath = remotePath;
|
||||
|
||||
cloneInfo(subWrapper, urlEntity);
|
||||
|
||||
if (mEntity.getUrls() == null) {
|
||||
mEntity.setUrls(new ArrayList<String>());
|
||||
}
|
||||
mEntity.getSubEntities().add(entity);
|
||||
mTaskWrapper.getSubTaskWrapper().add(subWrapper);
|
||||
}
|
||||
|
||||
private void cloneInfo(DTaskWrapper subWrapper, FtpUrlEntity urlEntity) {
|
||||
FtpTaskOption subOption = new FtpTaskOption();
|
||||
subOption.setUrlEntity(urlEntity);
|
||||
subOption.setCharSet(mTaskOption.getCharSet());
|
||||
subOption.setProxy(mTaskOption.getProxy());
|
||||
subOption.setClientConfig(mTaskOption.getClientConfig());
|
||||
subOption.setNewFileName(mTaskOption.getNewFileName());
|
||||
subOption.setProxy(mTaskOption.getProxy());
|
||||
subOption.setUploadInterceptor(mTaskOption.getUploadInterceptor());
|
||||
|
||||
subWrapper.setTaskOption(subOption);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查子任务是否已经存在,如果子任务存在,取消添加操作
|
||||
*
|
||||
* @param key url
|
||||
* @return true 子任务已存在,false 子任务不存在
|
||||
*/
|
||||
private boolean checkEntityExist(String key) {
|
||||
for (DTaskWrapper wrapper : mTaskWrapper.getSubTaskWrapper()) {
|
||||
if (wrapper.getKey().equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleFail(FTPClient client, String msg, Exception e, boolean needRetry) {
|
||||
super.handleFail(client, msg, e, needRetry);
|
||||
DeleteDGRecord.getInstance().deleteRecord(mTaskWrapper.getEntity(), true, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 android.os.Looper;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.group.AbsGroupLoader;
|
||||
import com.arialyy.aria.core.group.AbsSubDLoadUtil;
|
||||
import com.arialyy.aria.core.inf.IEntity;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.loader.IInfoTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
|
||||
/**
|
||||
* ftp 组合任务加载器
|
||||
*/
|
||||
final class FtpDGLoader extends AbsGroupLoader {
|
||||
FtpDGLoader(AbsTaskWrapper groupWrapper, IEventListener listener) {
|
||||
super(groupWrapper, listener);
|
||||
}
|
||||
|
||||
@Override protected void handlerTask(Looper looper) {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
mInfoTask.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) {
|
||||
FtpSubDLoaderUtil subUtil = new FtpSubDLoaderUtil(getScheduler(), needGetFileInfo, getKey());
|
||||
subUtil.setParams(wrapper, null);
|
||||
return subUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动子任务下载
|
||||
*/
|
||||
private void startSub() {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
onPostStart();
|
||||
|
||||
// ftp需要获取完成只任务信息才更新只任务数量
|
||||
getState().setSubSize(getWrapper().getSubTaskWrapper().size());
|
||||
|
||||
for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) {
|
||||
if (wrapper.getState() != IEntity.STATE_COMPLETE) {
|
||||
startSubLoader(createSubLoader(wrapper, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void addComponent(IInfoTask infoTask) {
|
||||
mInfoTask = infoTask;
|
||||
infoTask.setCallback(new IInfoTask.Callback() {
|
||||
@Override public void onSucceed(String key, CompleteInfo info) {
|
||||
if (info.code >= 200 && info.code < 300) {
|
||||
startSub();
|
||||
} else {
|
||||
ALog.e(TAG, "获取任务信息失败,code:" + info.code);
|
||||
getListener().onFail(false, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
|
||||
//getListener().onFail(needRetry, e);
|
||||
fail(e, needRetry);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.download.DGTaskWrapper;
|
||||
import com.arialyy.aria.core.group.AbsGroupLoader;
|
||||
import com.arialyy.aria.core.group.AbsGroupLoaderUtil;
|
||||
import com.arialyy.aria.core.loader.LoaderStructure;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/27.
|
||||
* ftp文件夹下载工具
|
||||
*/
|
||||
public final class FtpDGLoaderUtil extends AbsGroupLoaderUtil {
|
||||
|
||||
@Override protected AbsGroupLoader getLoader() {
|
||||
if (mLoader == null) {
|
||||
((AbsTaskWrapper) getTaskWrapper()).generateTaskOption(FtpTaskOption.class);
|
||||
mLoader = new FtpDGLoader((AbsTaskWrapper) getTaskWrapper(), getListener());
|
||||
}
|
||||
return mLoader;
|
||||
}
|
||||
|
||||
@Override protected LoaderStructure buildLoaderStructure() {
|
||||
LoaderStructure structure = new LoaderStructure();
|
||||
structure.addComponent(new FtpDGInfoTask((DGTaskWrapper) getTaskWrapper()));
|
||||
structure.accept(getLoader());
|
||||
return structure;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.download.DTaskWrapper;
|
||||
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.NormalLoader;
|
||||
import com.arialyy.aria.core.loader.NormalTTBuilder;
|
||||
import com.arialyy.aria.core.loader.NormalThreadStateManager;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-19
|
||||
*/
|
||||
public final class FtpDLoaderUtil extends AbsNormalLoaderUtil {
|
||||
|
||||
@Override public AbsNormalLoader getLoader() {
|
||||
if (mLoader == null){
|
||||
getTaskWrapper().generateTaskOption(FtpTaskOption.class);
|
||||
mLoader = new NormalLoader(getTaskWrapper(), getListener());
|
||||
}
|
||||
return mLoader ;
|
||||
}
|
||||
|
||||
public LoaderStructure BuildLoaderStructure() {
|
||||
LoaderStructure structure = new LoaderStructure();
|
||||
structure.addComponent(new FtpDRecordHandler((DTaskWrapper) getTaskWrapper()))
|
||||
.addComponent(new NormalThreadStateManager(getListener()))
|
||||
.addComponent(new FtpDFileInfoTask((DTaskWrapper) getTaskWrapper()))
|
||||
.addComponent(new NormalTTBuilder(getTaskWrapper(), new FtpDTTBuilderAdapter()));
|
||||
structure.accept(getLoader());
|
||||
return structure;
|
||||
}
|
||||
}
|
@ -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.ftp.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 FtpDRecordHandler extends RecordHandler {
|
||||
|
||||
FtpDRecordHandler(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 = Configuration.getInstance().downloadCfg.isUseBlock();
|
||||
record.taskType = ITaskWrapper.D_FTP;
|
||||
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,51 @@
|
||||
/*
|
||||
* 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.loader.AbsNormalTTBuilderAdapter;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.task.IThreadTaskAdapter;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.FileUtil;
|
||||
import java.io.File;
|
||||
|
||||
final class FtpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
|
||||
|
||||
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {
|
||||
return new FtpDThreadTaskAdapter(config);
|
||||
}
|
||||
|
||||
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
|
||||
if (!record.isBlock) {
|
||||
if (getTempFile().exists()) {
|
||||
FileUtil.deleteFile(getTempFile());
|
||||
}
|
||||
//CommonUtil.createFile(mTempFile.getPath());
|
||||
} else {
|
||||
for (int i = 0; i < totalThreadNum; i++) {
|
||||
File blockFile =
|
||||
new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i));
|
||||
if (blockFile.exists()) {
|
||||
ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i));
|
||||
FileUtil.deleteFile(blockFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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 aria.apache.commons.net.ftp.FTPClient;
|
||||
import aria.apache.commons.net.ftp.FTPReply;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.exception.AriaFTPException;
|
||||
import com.arialyy.aria.ftp.BaseFtpThreadTaskAdapter;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.BufferedRandomAccessFile;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-18
|
||||
*/
|
||||
final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
|
||||
FtpDThreadTaskAdapter(SubThreadConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override protected void handlerThreadTask() {
|
||||
if (getThreadRecord().isComplete) {
|
||||
handleComplete();
|
||||
return;
|
||||
}
|
||||
FTPClient client = null;
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
ALog.d(TAG,
|
||||
String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", getTaskWrapper().getKey(),
|
||||
getThreadRecord().threadId, getThreadRecord().startLocation,
|
||||
getThreadRecord().endLocation));
|
||||
client = createClient();
|
||||
if (client == null) {
|
||||
fail(new AriaFTPException("ftp client 创建失败"), false);
|
||||
return;
|
||||
}
|
||||
if (getThreadRecord().startLocation > 0) {
|
||||
client.setRestartOffset(getThreadRecord().startLocation);
|
||||
}
|
||||
//发送第二次指令时,还需要再做一次判断
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.COMMAND_OK) {
|
||||
fail(new AriaFTPException(
|
||||
String.format("获取文件信息错误,错误码为:%s,msg:%s", reply, client.getReplyString())), false);
|
||||
client.disconnect();
|
||||
return;
|
||||
}
|
||||
String remotePath =
|
||||
CommonUtil.convertFtpChar(charSet, mTaskOption.getUrlEntity().remotePath);
|
||||
ALog.i(TAG, String.format("remotePath【%s】", remotePath));
|
||||
is = client.retrieveFileStream(remotePath);
|
||||
reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositivePreliminary(reply)) {
|
||||
fail(new AriaFTPException(
|
||||
String.format("获取流失败,错误码为:%s,msg:%s", reply, client.getReplyString())), true);
|
||||
client.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (getThreadConfig().isBlock) {
|
||||
readDynamicFile(is);
|
||||
} else {
|
||||
readNormal(is);
|
||||
handleComplete();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
|
||||
} catch (Exception e) {
|
||||
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), false);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
closeClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理线程完成的情况
|
||||
*/
|
||||
private void handleComplete() {
|
||||
if (getThreadTask().isBreak()) {
|
||||
return;
|
||||
}
|
||||
if (!getThreadTask().checkBlock()) {
|
||||
return;
|
||||
}
|
||||
complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态长度文件读取方式
|
||||
*/
|
||||
private void readDynamicFile(InputStream is) {
|
||||
FileOutputStream fos = null;
|
||||
FileChannel foc = null;
|
||||
ReadableByteChannel fic = null;
|
||||
try {
|
||||
int len;
|
||||
fos = new FileOutputStream(getThreadConfig().tempFile, true);
|
||||
foc = fos.getChannel();
|
||||
fic = Channels.newChannel(is);
|
||||
ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize());
|
||||
while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) {
|
||||
if (getThreadTask().isBreak()) {
|
||||
break;
|
||||
}
|
||||
if (mSpeedBandUtil != null) {
|
||||
mSpeedBandUtil.limitNextBytes(len);
|
||||
}
|
||||
if (getRangeProgress() + len >= getThreadRecord().endLocation) {
|
||||
len = (int) (getThreadRecord().endLocation - getRangeProgress());
|
||||
bf.flip();
|
||||
fos.write(bf.array(), 0, len);
|
||||
bf.compact();
|
||||
progress(len);
|
||||
break;
|
||||
} else {
|
||||
bf.flip();
|
||||
foc.write(bf);
|
||||
bf.compact();
|
||||
progress(len);
|
||||
}
|
||||
}
|
||||
handleComplete();
|
||||
} catch (IOException e) {
|
||||
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
|
||||
} finally {
|
||||
try {
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
if (foc != null) {
|
||||
foc.close();
|
||||
}
|
||||
if (fic != null) {
|
||||
fic.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多线程写文件方式
|
||||
*/
|
||||
private void readNormal(InputStream is) {
|
||||
BufferedRandomAccessFile file = null;
|
||||
try {
|
||||
file =
|
||||
new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd",
|
||||
getTaskConfig().getBuffSize());
|
||||
if (getThreadRecord().startLocation > 0) {
|
||||
file.seek(getThreadRecord().startLocation);
|
||||
}
|
||||
byte[] buffer = new byte[getTaskConfig().getBuffSize()];
|
||||
int len;
|
||||
while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) {
|
||||
if (getThreadTask().isBreak()) {
|
||||
break;
|
||||
}
|
||||
if (mSpeedBandUtil != null) {
|
||||
mSpeedBandUtil.limitNextBytes(len);
|
||||
}
|
||||
if (getRangeProgress() + len >= getThreadRecord().endLocation) {
|
||||
len = (int) (getThreadRecord().endLocation - getRangeProgress());
|
||||
file.write(buffer, 0, len);
|
||||
progress(len);
|
||||
break;
|
||||
} else {
|
||||
file.write(buffer, 0, len);
|
||||
progress(len);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
fail(new AriaFTPException(String.format("下载失败【%s】", getThreadConfig().url), e), true);
|
||||
} finally {
|
||||
try {
|
||||
if (file != null) {
|
||||
file.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 android.os.Handler;
|
||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||
import com.arialyy.aria.core.group.AbsSubDLoadUtil;
|
||||
import com.arialyy.aria.core.group.SubRecordHandler;
|
||||
import com.arialyy.aria.core.loader.GroupSubThreadStateManager;
|
||||
import com.arialyy.aria.core.loader.LoaderStructure;
|
||||
import com.arialyy.aria.core.loader.NormalTTBuilder;
|
||||
import com.arialyy.aria.core.loader.SubLoader;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-28
|
||||
*/
|
||||
final class FtpSubDLoaderUtil extends AbsSubDLoadUtil {
|
||||
/**
|
||||
* @param schedulers 调度器
|
||||
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
|
||||
*/
|
||||
FtpSubDLoaderUtil(Handler schedulers, boolean needGetInfo, String parentKey) {
|
||||
super(schedulers, needGetInfo, parentKey);
|
||||
}
|
||||
|
||||
@Override protected SubLoader getLoader() {
|
||||
if (mDLoader == null) {
|
||||
mDLoader = new SubLoader(getWrapper(), getSchedulers());
|
||||
mDLoader.setNeedGetInfo(isNeedGetInfo());
|
||||
mDLoader.setParentKey(getParentKey());
|
||||
}
|
||||
return mDLoader;
|
||||
}
|
||||
|
||||
@Override protected LoaderStructure buildLoaderStructure() {
|
||||
LoaderStructure structure = new LoaderStructure();
|
||||
structure.addComponent(new SubRecordHandler(getWrapper()))
|
||||
.addComponent(new GroupSubThreadStateManager(getSchedulers(),getKey()))
|
||||
.addComponent(new NormalTTBuilder(getWrapper(), new FtpDTTBuilderAdapter()))
|
||||
.addComponent(new FtpDFileInfoTask(getWrapper()));
|
||||
structure.accept(getLoader());
|
||||
return structure;
|
||||
}
|
||||
}
|
@ -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.ftp.upload;
|
||||
|
||||
import com.arialyy.aria.util.BufferedRandomAccessFile;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2017/9/26.
|
||||
* BufferedRandomAccessFile 转 InputStream 适配器
|
||||
*/
|
||||
final class FtpFISAdapter extends InputStream {
|
||||
|
||||
private BufferedRandomAccessFile mIs;
|
||||
private ProgressCallback mCallback;
|
||||
private int count;
|
||||
|
||||
interface ProgressCallback {
|
||||
void onProgressCallback(byte[] buffer, int byteOffset, int byteCount) throws IOException;
|
||||
}
|
||||
|
||||
FtpFISAdapter(BufferedRandomAccessFile is, ProgressCallback callback) {
|
||||
mIs = is;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
FtpFISAdapter(BufferedRandomAccessFile is) {
|
||||
mIs = is;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
mIs.close();
|
||||
}
|
||||
|
||||
@Override public int read() throws IOException {
|
||||
return mIs.read();
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buffer) throws IOException {
|
||||
count = mIs.read(buffer);
|
||||
if (mCallback != null) {
|
||||
mCallback.onProgressCallback(buffer, 0, count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buffer, int byteOffset, int byteCount)
|
||||
throws IOException {
|
||||
count = mIs.read(buffer, byteOffset, byteCount);
|
||||
if (mCallback != null) {
|
||||
mCallback.onProgressCallback(buffer, byteOffset, byteCount);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override public long skip(long byteCount) throws IOException {
|
||||
return mIs.skipBytes((int) byteCount);
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.upload;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import aria.apache.commons.net.ftp.FTPClient;
|
||||
import aria.apache.commons.net.ftp.FTPFile;
|
||||
import aria.apache.commons.net.ftp.FTPReply;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.processor.FtpInterceptHandler;
|
||||
import com.arialyy.aria.core.processor.IFtpUploadInterceptor;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.ftp.AbsFtpInfoTask;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/9/26.
|
||||
* 单任务上传远程服务器文件信息
|
||||
*/
|
||||
final class FtpUFileInfoTask extends AbsFtpInfoTask<UploadEntity, UTaskWrapper> {
|
||||
static final int CODE_COMPLETE = 0xab1;
|
||||
private boolean isComplete = false;
|
||||
private String remotePath;
|
||||
private FTPFile ftpFile;
|
||||
|
||||
FtpUFileInfoTask(UTaskWrapper taskEntity) {
|
||||
super(taskEntity);
|
||||
}
|
||||
|
||||
@Override protected String getRemotePath() {
|
||||
return remotePath == null ?
|
||||
mTaskOption.getUrlEntity().remotePath + "/" + mEntity.getFileName() : remotePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handelFileInfo(FTPClient client, FTPFile[] files, String convertedRemotePath)
|
||||
throws IOException {
|
||||
// 处理拦截功能
|
||||
if (!onInterceptor(client, files)) {
|
||||
closeClient(client);
|
||||
ALog.d(TAG, "拦截器处理完成任务");
|
||||
return;
|
||||
}
|
||||
|
||||
handleFile(client, getRemotePath(), files.length == 0 ? null : files[0]);
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
//服务器上没有该文件路径,表示该任务为新的上传任务
|
||||
mTaskWrapper.setNewTask(true);
|
||||
}
|
||||
mTaskWrapper.setCode(reply);
|
||||
onPreComplete(reply);
|
||||
mEntity.update();
|
||||
}
|
||||
|
||||
@Override protected boolean onInterceptor(FTPClient client, FTPFile[] ftpFiles) {
|
||||
// 旧任务将不做处理,否则断点续传上传将失效
|
||||
//if (!mTaskWrapper.isNewTask()) {
|
||||
// ALog.d(TAG, "任务是旧任务,忽略该拦截器");
|
||||
// return true;
|
||||
//}
|
||||
try {
|
||||
IFtpUploadInterceptor interceptor = mTaskOption.getUploadInterceptor();
|
||||
if (interceptor != null) {
|
||||
/*
|
||||
* true 使用拦截器,false 不使用拦截器
|
||||
*/
|
||||
List<String> files = new ArrayList<>();
|
||||
for (FTPFile ftpFile : ftpFiles) {
|
||||
if (ftpFile.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
files.add(ftpFile.getName());
|
||||
}
|
||||
|
||||
FtpInterceptHandler interceptHandler = interceptor.onIntercept(mEntity, files);
|
||||
|
||||
/*
|
||||
* 处理远端有同名文件的情况
|
||||
*/
|
||||
if (files.contains(mEntity.getFileName())) {
|
||||
if (interceptHandler.isCoverServerFile()) {
|
||||
ALog.i(TAG, String.format("远端已拥有同名文件,将覆盖该文件,文件名:%s", mEntity.getFileName()));
|
||||
boolean b = client.deleteFile(CommonUtil.convertFtpChar(charSet, getRemotePath()));
|
||||
ALog.d(TAG,
|
||||
String.format("删除文件%s,code: %s, msg: %s", b ? "成功" : "失败", client.getReplyCode(),
|
||||
client.getReplyString()));
|
||||
} else if (!TextUtils.isEmpty(interceptHandler.getNewFileName())) {
|
||||
ALog.i(TAG, String.format("远端已拥有同名文件,将修改remotePath,原文件名:%s,新文件名:%s",
|
||||
mEntity.getFileName(), interceptHandler.getNewFileName()));
|
||||
remotePath = mTaskOption.getUrlEntity().remotePath
|
||||
+ "/"
|
||||
+ interceptHandler.getNewFileName();
|
||||
mTaskOption.setNewFileName(interceptHandler.getNewFileName());
|
||||
|
||||
closeClient(client);
|
||||
run();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果服务器的文件长度和本地上传文件的文件长度一致,则任务任务已完成。
|
||||
* 否则重新修改保存的停止位置,这是因为outputStream是读不到服务器是否成功写入的。
|
||||
* 而threadTask的保存的停止位置是File的InputStream的,所有就会导致两端停止位置不一致
|
||||
*
|
||||
* @param remotePath ftp服务器文件夹路径
|
||||
* @param ftpFile ftp服务器上对应的文件
|
||||
*/
|
||||
@Override protected void handleFile(FTPClient client,String remotePath, FTPFile ftpFile) {
|
||||
super.handleFile(client, remotePath, ftpFile);
|
||||
this.ftpFile = ftpFile;
|
||||
if (ftpFile != null && ftpFile.getSize() == mEntity.getFileSize()) {
|
||||
isComplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void onPreComplete(int code) {
|
||||
super.onPreComplete(code);
|
||||
CompleteInfo info = new CompleteInfo(isComplete ? CODE_COMPLETE : code, mTaskWrapper);
|
||||
info.obj = ftpFile;
|
||||
onSucceed(info);
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.upload;
|
||||
|
||||
import android.os.Handler;
|
||||
import aria.apache.commons.net.ftp.FTPFile;
|
||||
import com.arialyy.aria.core.common.AbsEntity;
|
||||
import com.arialyy.aria.core.common.CompleteInfo;
|
||||
import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.loader.IInfoTask;
|
||||
import com.arialyy.aria.core.loader.IRecordHandler;
|
||||
import com.arialyy.aria.core.loader.NormalLoader;
|
||||
import com.arialyy.aria.core.manager.ThreadTaskManager;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.exception.AriaException;
|
||||
|
||||
final class FtpULoader extends NormalLoader<UTaskWrapper> {
|
||||
private FTPFile ftpFile;
|
||||
|
||||
FtpULoader(UTaskWrapper wrapper, IEventListener listener) {
|
||||
super(wrapper, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startThreadTask() {
|
||||
if (isBreak()) {
|
||||
return;
|
||||
}
|
||||
// 检查记录
|
||||
((FtpURecordHandler) mRecordHandler).setFtpFile(ftpFile);
|
||||
if (mRecordHandler.checkTaskCompleted()) {
|
||||
mRecord.deleteData();
|
||||
isComplete = true;
|
||||
getListener().onComplete();
|
||||
return;
|
||||
}
|
||||
mRecord = mRecordHandler.getRecord(getFileSize());
|
||||
|
||||
// 初始化线程状态管理器
|
||||
mStateManager.setLooper(mRecord, getLooper());
|
||||
getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord,
|
||||
new Handler(getLooper(), mStateManager.getHandlerCallback())));
|
||||
mStateManager.updateCurrentProgress(getEntity().getCurrentProgress());
|
||||
|
||||
if (mStateManager.getCurrentProgress() > 0) {
|
||||
getListener().onResume(mStateManager.getCurrentProgress());
|
||||
} else {
|
||||
getListener().onStart(mStateManager.getCurrentProgress());
|
||||
}
|
||||
|
||||
startTimer();
|
||||
|
||||
// 启动线程任务
|
||||
for (IThreadTask threadTask : getTaskList()) {
|
||||
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void addComponent(IInfoTask infoTask) {
|
||||
mInfoTask = infoTask;
|
||||
infoTask.setCallback(new IInfoTask.Callback() {
|
||||
@Override public void onSucceed(String key, CompleteInfo info) {
|
||||
if (info.code == FtpUFileInfoTask.CODE_COMPLETE) {
|
||||
getListener().onComplete();
|
||||
} else {
|
||||
ftpFile = (FTPFile) info.obj;
|
||||
startThreadTask();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
|
||||
getListener().onFail(needRetry, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void addComponent(IRecordHandler recordHandler) {
|
||||
mRecordHandler = recordHandler;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.upload;
|
||||
|
||||
import com.arialyy.aria.core.TaskRecord;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoader;
|
||||
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
|
||||
import com.arialyy.aria.core.loader.AbsNormalTTBuilderAdapter;
|
||||
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.loader.UploadThreadStateManager;
|
||||
import com.arialyy.aria.core.task.IThreadTaskAdapter;
|
||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||
import com.arialyy.aria.ftp.FtpTaskOption;
|
||||
|
||||
/**
|
||||
* @author lyy
|
||||
* Date: 2019-09-19
|
||||
*/
|
||||
public final class FtpULoaderUtil extends AbsNormalLoaderUtil {
|
||||
|
||||
@Override public AbsNormalLoader getLoader() {
|
||||
if (mLoader == null) {
|
||||
getTaskWrapper().generateTaskOption(FtpTaskOption.class);
|
||||
mLoader = new FtpULoader((UTaskWrapper) getTaskWrapper(), getListener());
|
||||
}
|
||||
return mLoader;
|
||||
}
|
||||
|
||||
@Override public LoaderStructure BuildLoaderStructure() {
|
||||
LoaderStructure structure = new LoaderStructure();
|
||||
structure.addComponent(new FtpURecordHandler((UTaskWrapper) getTaskWrapper()))
|
||||
// .addComponent(new NormalThreadStateManager(getListener()))
|
||||
.addComponent(new UploadThreadStateManager(getListener()))
|
||||
.addComponent(new FtpUFileInfoTask((UTaskWrapper) getTaskWrapper()))
|
||||
.addComponent(new NormalTTBuilder(getTaskWrapper(), new AbsNormalTTBuilderAdapter() {
|
||||
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {
|
||||
return new FtpUThreadTaskAdapter(config);
|
||||
}
|
||||
|
||||
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
structure.accept(getLoader());
|
||||
return structure;
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.upload;
|
||||
|
||||
import aria.apache.commons.net.ftp.FTPFile;
|
||||
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.wrapper.ITaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.RecordUtil;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 上传任务记录处理器
|
||||
*/
|
||||
final class FtpURecordHandler extends RecordHandler {
|
||||
private FTPFile ftpFile;
|
||||
|
||||
FtpURecordHandler(UTaskWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
void setFtpFile(FTPFile ftpFile) {
|
||||
this.ftpFile = ftpFile;
|
||||
}
|
||||
|
||||
@Override public void handlerTaskRecord(TaskRecord record) {
|
||||
if (record.threadRecords == null || record.threadRecords.isEmpty()) {
|
||||
record.threadRecords = new ArrayList<>();
|
||||
record.threadRecords.add(
|
||||
createThreadRecord(record, 0, ftpFile == null ? 0 : ftpFile.getSize(), getFileSize()));
|
||||
}
|
||||
|
||||
if (ftpFile != null) {
|
||||
//远程文件已完成
|
||||
if (ftpFile.getSize() == getFileSize()) {
|
||||
record.threadRecords.get(0).isComplete = true;
|
||||
ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】");
|
||||
} else if (ftpFile.getSize() == 0) {
|
||||
getWrapper().setNewTask(true);
|
||||
ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】,但文件长度为0,重新上传该文件");
|
||||
} else {
|
||||
ALog.w(TAG, "FTP服务器已存在未完成的文件【"
|
||||
+ ftpFile.getName()
|
||||
+ ",size: "
|
||||
+ ftpFile.getSize()
|
||||
+ "】"
|
||||
+ "尝试从位置:"
|
||||
+ (ftpFile.getSize() - 1)
|
||||
+ "开始上传");
|
||||
getWrapper().setNewTask(false);
|
||||
|
||||
// 修改记录
|
||||
ThreadRecord threadRecord = record.threadRecords.get(0);
|
||||
//修改本地保存的停止地址为服务器上对应文件的大小
|
||||
threadRecord.startLocation = ftpFile.getSize() - 1;
|
||||
}
|
||||
} else {
|
||||
ALog.d(TAG, "FTP服务器上不存在该文件");
|
||||
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_FTP;
|
||||
record.isGroupRecord = getEntity().isGroupChild();
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override public int initTaskThreadNum() {
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.upload;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import aria.apache.commons.net.ftp.FTPClient;
|
||||
import aria.apache.commons.net.ftp.FTPReply;
|
||||
import aria.apache.commons.net.ftp.OnFtpInputStreamListener;
|
||||
import com.arialyy.aria.core.common.SubThreadConfig;
|
||||
import com.arialyy.aria.core.upload.UploadEntity;
|
||||
import com.arialyy.aria.exception.AriaFTPException;
|
||||
import com.arialyy.aria.ftp.BaseFtpThreadTaskAdapter;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.BufferedRandomAccessFile;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/28. D_FTP 单线程上传任务,需要FTP 服务器给用户打开append和write的权限
|
||||
*/
|
||||
final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
private String dir, remotePath;
|
||||
private boolean storeSuccess = false;
|
||||
private ScheduledThreadPoolExecutor timer;
|
||||
private FTPClient client = null;
|
||||
private boolean isTimeOut = true;
|
||||
private FtpFISAdapter fa;
|
||||
|
||||
FtpUThreadTaskAdapter(SubThreadConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override protected void handlerThreadTask() {
|
||||
BufferedRandomAccessFile file = null;
|
||||
try {
|
||||
ALog.d(TAG,
|
||||
String.format("任务【%s】线程__%s__开始上传【开始位置 : %s,结束位置:%s】", getEntity().getKey(),
|
||||
getThreadRecord().threadId, getThreadRecord().startLocation,
|
||||
getThreadRecord().endLocation));
|
||||
client = createClient();
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
initPath();
|
||||
boolean b = client.makeDirectory(dir);
|
||||
if (!b) {
|
||||
ALog.w(TAG, String.format("创建目录失败,错误码为:%s, msg:%s", client.getReplyCode(),
|
||||
client.getReplyString()));
|
||||
}
|
||||
client.changeWorkingDirectory(dir);
|
||||
client.setRestartOffset(getThreadRecord().startLocation);
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.FILE_ACTION_OK) {
|
||||
fail(new AriaFTPException(
|
||||
String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply,
|
||||
client.getReplyString(), getEntity().getFilePath())), false);
|
||||
client.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
file =
|
||||
new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd",
|
||||
getTaskConfig().getBuffSize());
|
||||
if (getThreadRecord().startLocation > 0) {
|
||||
file.seek(getThreadRecord().startLocation);
|
||||
}
|
||||
boolean complete = upload(file);
|
||||
if (getThreadTask().isBreak()) {
|
||||
return;
|
||||
}
|
||||
if (!complete) {
|
||||
fail(new AriaFTPException("ftp文件上传失败"), false);
|
||||
return;
|
||||
}
|
||||
ALog.i(TAG,
|
||||
String.format("任务【%s】线程__%s__上传完毕", getEntity().getKey(), getThreadRecord().threadId));
|
||||
complete();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
fail(new AriaFTPException(
|
||||
String.format("上传失败,filePath: %s, uploadUrl: %s", getEntity().getFilePath(),
|
||||
getThreadConfig().url)), true);
|
||||
} catch (Exception e) {
|
||||
fail(new AriaFTPException(null, e), false);
|
||||
} finally {
|
||||
try {
|
||||
if (file != null) {
|
||||
file.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
closeClient(client);
|
||||
closeTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private UploadEntity getEntity() {
|
||||
return (UploadEntity) getTaskWrapper().getEntity();
|
||||
}
|
||||
|
||||
private void initPath() throws UnsupportedEncodingException {
|
||||
dir = CommonUtil.convertFtpChar(charSet, mTaskOption.getUrlEntity().remotePath);
|
||||
|
||||
String fileName = TextUtils.isEmpty(mTaskOption.getNewFileName()) ? getEntity().getFileName()
|
||||
: mTaskOption.getNewFileName();
|
||||
|
||||
remotePath =
|
||||
CommonUtil.convertFtpChar(charSet,
|
||||
String.format("%s/%s", mTaskOption.getUrlEntity().remotePath, fileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动监听定时器,当网络断开时,如果该任务的FTP服务器的传输线程没有断开,当客户端重新连接时,客户端将无法发送数据到服务端
|
||||
* 每隔10s检查一次。
|
||||
*/
|
||||
private void startTimer() {
|
||||
timer = new ScheduledThreadPoolExecutor(1);
|
||||
timer.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override public void run() {
|
||||
try {
|
||||
if (isTimeOut) {
|
||||
fail(new AriaFTPException("socket连接失败,该问题一般出现于网络断开,客户端重新连接,"
|
||||
+ "但是服务器端无法创建socket缺没有返回错误码的情况。"), false);
|
||||
if (fa != null) {
|
||||
fa.close();
|
||||
}
|
||||
closeTimer();
|
||||
}
|
||||
isTimeOut = true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, 10, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void closeTimer() {
|
||||
ALog.d(TAG, "closeTimer");
|
||||
if (timer != null && !timer.isShutdown()) {
|
||||
timer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*
|
||||
* @return {@code true}上传成功、{@code false} 上传失败
|
||||
*/
|
||||
private boolean upload(final BufferedRandomAccessFile bis)
|
||||
throws IOException {
|
||||
fa = new FtpFISAdapter(bis);
|
||||
storeSuccess = false;
|
||||
startTimer();
|
||||
try {
|
||||
ALog.d(TAG, String.format("remotePath: %s", remotePath));
|
||||
storeSuccess = client.storeFile(remotePath, fa, new OnFtpInputStreamListener() {
|
||||
boolean isStoped = false;
|
||||
|
||||
@Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred,
|
||||
int bytesTransferred, long streamSize) {
|
||||
try {
|
||||
isTimeOut = false;
|
||||
if (getThreadTask().isBreak() && !isStoped) {
|
||||
isStoped = true;
|
||||
client.abor();
|
||||
return;
|
||||
}
|
||||
if (mSpeedBandUtil != null) {
|
||||
mSpeedBandUtil.limitNextBytes(bytesTransferred);
|
||||
}
|
||||
progress(bytesTransferred);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
storeSuccess = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
String msg = String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", client.getReply(),
|
||||
client.getReplyString(), getEntity().getFilePath());
|
||||
e.printStackTrace();
|
||||
if (e.getMessage().contains("AriaIOException caught while copying")) {
|
||||
e.printStackTrace();
|
||||
} else {
|
||||
fail(new AriaFTPException(msg, e), !storeSuccess);
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
fa.close();
|
||||
}
|
||||
int reply = client.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
if (reply != FTPReply.TRANSFER_ABORTED) {
|
||||
fail(new AriaFTPException(
|
||||
String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply, client.getReplyString(),
|
||||
getEntity().getFilePath())), false);
|
||||
}
|
||||
closeClient(client);
|
||||
return false;
|
||||
}
|
||||
return storeSuccess;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user