源代码备份

This commit is contained in:
TC999
2024-08-20 16:54:35 +08:00
parent c4db18da39
commit e2a5f92e23
791 changed files with 90314 additions and 2 deletions

View File

@ -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;
}
}
}

View File

@ -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服务器错误码为%smsg:%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);
}
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
});
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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("获取文件信息错误,错误码为:%smsg%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("获取流失败,错误码为:%smsg%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();
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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("删除文件%scode: %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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}