源代码备份

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,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arialyy.aria.http" />

View File

@ -0,0 +1,41 @@
/*
* 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.http;
import com.arialyy.aria.core.common.AbsNormalEntity;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
/**
* @author lyy
* Date: 2019-09-22
*/
public abstract class BaseHttpThreadTaskAdapter extends AbsThreadTaskAdapter {
protected HttpTaskOption mTaskOption;
protected BaseHttpThreadTaskAdapter(SubThreadConfig config) {
super(config);
mTaskOption = (HttpTaskOption) getTaskWrapper().getTaskOption();
}
protected String getFileName() {
return getEntity().getFileName();
}
protected AbsNormalEntity getEntity() {
return (AbsNormalEntity) getTaskWrapper().getEntity();
}
}

View File

@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.http;
import com.arialyy.aria.util.ALog;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
// Data is expected to be a series of data chunks in the form <chunk size><chunk bytes><chunk size><chunk bytes>
// The final data chunk should be a 0-length chunk which will indicate end of input.
@Deprecated
public class ChunkedInputStream extends InputStream {
private static final String TAG = "ChunkedInputStream";
private DataInputStream din;
private int unreadBytes = 0; // Bytes remaining in the current chunk of data
private byte[] singleByte = new byte[1];
private boolean endOfData = false;
private String id;
public ChunkedInputStream(InputStream in, String id) {
din = new DataInputStream(in);
this.id = id;
ALog.d(TAG, String.format("Creating chunked input for %s", id));
}
@Override
public void close() throws IOException {
ALog.d(TAG, String.format("%s: Closing chunked input.", id));
din.close();
}
@Override
public int read() throws IOException {
int bytesRead = read(singleByte, 0, 1);
return (bytesRead == -1) ? -1 : (int) singleByte[0];
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = 0;
if (len < 0) {
throw new IllegalArgumentException(id + ": Negative read length");
} else if (len == 0) {
return 0;
}
// If there is a current unread chunk, read from that, or else get the next chunk.
if (unreadBytes == 0) {
try {
// Find the next chunk size
unreadBytes = din.readInt();
if (ALog.DEBUG) {
ALog.d(TAG, String.format("%s: Chunk size %s", id, unreadBytes));
}
if (unreadBytes == 0) {
ALog.d(TAG, String.format("%s: Hit end of data", id));
endOfData = true;
return -1;
}
} catch (IOException err) {
throw new IOException(id + ": Error while attempting to read chunk length", err);
}
}
int bytesToRead = Math.min(len, unreadBytes);
try {
din.readFully(b, off, bytesToRead);
} catch (IOException err) {
throw new IOException(
id + ": Error while attempting to read " + bytesToRead + " bytes from current chunk",
err);
}
unreadBytes -= bytesToRead;
bytesRead += bytesToRead;
return bytesRead;
}
public boolean isEndOfData() {
return endOfData;
}
}

View File

@ -0,0 +1,198 @@
/*
* 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.http;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.ProtocolType;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.SSLContextUtil;
import java.io.IOException;
import java.io.InputStream;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
/**
* Created by lyy on 2017/1/18. 链接帮助类
*/
public final class ConnectionHelp {
private static final String TAG = "ConnectionHelp";
/**
* 处理url参数
*/
public static URL handleUrl(String url, HttpTaskOption taskDelegate)
throws MalformedURLException {
Map<String, String> params = taskDelegate.getParams();
if (params != null && taskDelegate.getRequestEnum() == RequestEnum.GET) {
if (url.contains("?")) {
ALog.e(TAG, String.format("设置参数失败url中已经有?url: %s", url));
return new URL(CommonUtil.convertUrl(url));
}
StringBuilder sb = new StringBuilder();
sb.append(url).append("?");
Set<String> keys = params.keySet();
for (String key : keys) {
sb.append(key).append("=").append(URLEncoder.encode(params.get(key))).append("&");
}
String temp = sb.toString();
temp = temp.substring(0, temp.length() - 1);
return new URL(CommonUtil.convertUrl(temp));
} else {
return new URL(CommonUtil.convertUrl(url));
}
}
/**
* 转换HttpUrlConnect的inputStream流
*
* @return {@link GZIPInputStream}、{@link InflaterInputStream}
* @throws IOException
*/
public static InputStream convertInputStream(HttpURLConnection connection) throws IOException {
String encoding = connection.getHeaderField("Content-Encoding");
if (TextUtils.isEmpty(encoding)) {
return connection.getInputStream();
}
if (encoding.contains("gzip")) {
return new GZIPInputStream(connection.getInputStream());
} else if (encoding.contains("deflate")) {
return new InflaterInputStream(connection.getInputStream());
} else {
return connection.getInputStream();
}
}
/**
* 处理链接
*
* @throws IOException
*/
public static HttpURLConnection handleConnection(URL url, HttpTaskOption taskDelegate)
throws IOException {
HttpURLConnection conn;
URLConnection urlConn;
if (taskDelegate.getProxy() != null) {
urlConn = url.openConnection(taskDelegate.getProxy());
} else {
urlConn = url.openConnection();
}
if (urlConn instanceof HttpsURLConnection) {
AriaConfig config = AriaConfig.getInstance();
conn = (HttpsURLConnection) urlConn;
SSLContext sslContext =
SSLContextUtil.getSSLContextFromAssets(config.getDConfig().getCaName(),
config.getDConfig().getCaPath(), ProtocolType.Default);
if (sslContext == null) {
sslContext = SSLContextUtil.getDefaultSLLContext(ProtocolType.Default);
}
SSLSocketFactory ssf = sslContext.getSocketFactory();
((HttpsURLConnection) conn).setSSLSocketFactory(ssf);
((HttpsURLConnection) conn).setHostnameVerifier(SSLContextUtil.HOSTNAME_VERIFIER);
} else {
conn = (HttpURLConnection) urlConn;
}
return conn;
}
/**
* 设置头部参数
*/
public static HttpURLConnection setConnectParam(HttpTaskOption delegate, HttpURLConnection conn) {
if (delegate.getRequestEnum() == RequestEnum.POST) {
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
}
Set<String> keys = null;
if (delegate.getHeaders() != null && delegate.getHeaders().size() > 0) {
keys = delegate.getHeaders().keySet();
for (String key : keys) {
conn.setRequestProperty(key, delegate.getHeaders().get(key));
}
}
if (conn.getRequestProperty("Accept-Language") == null) {
conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7");
}
if (conn.getRequestProperty("Accept-Encoding") == null) {
conn.setRequestProperty("Accept-Encoding", "identity");
}
if (conn.getRequestProperty("Accept-Charset") == null) {
conn.setRequestProperty("Accept-Charset", "UTF-8");
}
if (conn.getRequestProperty("Connection") == null) {
conn.setRequestProperty("Connection", "Keep-Alive");
}
if (conn.getRequestProperty("Charset") == null) {
conn.setRequestProperty("Charset", "UTF-8");
}
if (conn.getRequestProperty("User-Agent") == null) {
conn.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
}
if (conn.getRequestProperty("Accept") == null) {
StringBuilder accept = new StringBuilder();
accept.append("image/gif, ")
.append("image/jpeg, ")
.append("image/pjpeg, ")
.append("image/webp, ")
.append("image/apng, ")
.append("application/xml, ")
.append("application/xaml+xml, ")
.append("application/xhtml+xml, ")
.append("application/x-shockwave-flash, ")
.append("application/x-ms-xbap, ")
.append("application/x-ms-application, ")
.append("application/msword, ")
.append("application/vnd.ms-excel, ")
.append("application/vnd.ms-xpsdocument, ")
.append("application/vnd.ms-powerpoint, ")
.append("application/signed-exchange, ")
.append("text/plain, ")
.append("text/html, ")
.append("*/*");
conn.setRequestProperty("Accept", accept.toString());
}
//302获取重定向地址
conn.setInstanceFollowRedirects(false);
CookieManager manager = delegate.getCookieManager();
if (manager != null) {
CookieStore store = manager.getCookieStore();
if (store != null && store.getCookies().size() > 0) {
conn.setRequestProperty("Cookie",
TextUtils.join(";", store.getCookies()));
}
}
return conn;
}
}

View File

@ -0,0 +1,111 @@
/*
* 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.http;
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.DownloadEntity;
import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.RecordUtil;
import java.util.ArrayList;
/**
* @author lyy
* Date: 2019-09-23
*/
public final class HttpRecordHandler extends RecordHandler {
public HttpRecordHandler(AbsTaskWrapper wrapper) {
super(wrapper);
}
@Override public void handlerTaskRecord(TaskRecord record) {
RecordHelper helper = new RecordHelper(getWrapper(), record);
if (getWrapper().isSupportBP() && record.threadNum > 1) {
if (record.isBlock) {
helper.handleBlockRecord();
} else {
helper.handleMultiRecord();
}
} else if (!getWrapper().isSupportBP()) {
helper.handleNoSupportBPRecord();
} else {
helper.handleSingleThreadRecord();
}
}
@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;
int requestType = getWrapper().getRequestType();
if (requestType == ITaskWrapper.D_HTTP || requestType == ITaskWrapper.DG_HTTP) {
record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock();
} else {
record.isBlock = false;
}
record.taskType = requestType;
record.isGroupRecord = getEntity().isGroupChild();
if (record.isGroupRecord) {
if (getEntity() instanceof DownloadEntity) {
record.dGroupHash = ((DownloadEntity) getEntity()).getGroupHash();
}
}
return record;
}
@Override public int initTaskThreadNum() {
int requestTpe = getWrapper().getRequestType();
if (requestTpe == ITaskWrapper.U_HTTP
|| (requestTpe == ITaskWrapper.D_HTTP && (!getWrapper().isSupportBP())
|| ((HttpTaskOption) getWrapper().getTaskOption()).isChunked())) {
return 1;
}
int threadNum = Configuration.getInstance().downloadCfg.getThreadNum();
return getFileSize() <= IRecordHandler.SUB_LEN
|| getEntity().isGroupChild()
|| threadNum == 1
? 1
: threadNum;
}
}

View File

@ -0,0 +1,191 @@
/*
* 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.http;
import android.text.TextUtils;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.inf.ITaskOption;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter;
import com.arialyy.aria.core.processor.IHttpFileNameAdapter;
import java.lang.ref.SoftReference;
import java.net.CookieManager;
import java.net.Proxy;
import java.util.HashMap;
import java.util.Map;
/**
* Http任务设置的信息cookie、请求参数
*/
public final class HttpTaskOption implements ITaskOption {
private CookieManager cookieManager;
/**
* 请求参数
*/
private Map<String, String> params;
/**
* http 请求头
*/
private Map<String, String> headers = new HashMap<>();
/**
* 字符编码,默认为"utf-8"
*/
private String charSet = "utf-8";
/**
* 网络请求类型
*/
private RequestEnum requestEnum = RequestEnum.GET;
/**
* 是否使用服务器通过content-disposition传递的文件名内容格式{@code attachment; filename="filename.jpg"} {@code true}
* 使用
*/
private boolean useServerFileName = false;
/**
* 重定向链接
*/
private String redirectUrl = "";
/**
* 是否是chunk模式
*/
private boolean isChunked = false;
/**
* 文件上传需要的key
*/
private String attachment;
private Proxy proxy;
/**
* 文件上传表单
*/
private Map<String, String> formFields = new HashMap<>();
private SoftReference<IHttpFileLenAdapter> fileLenAdapter;
private SoftReference<IHttpFileNameAdapter> fileNameAdapter;
public IHttpFileLenAdapter getFileLenAdapter() {
return fileLenAdapter == null ? null : fileLenAdapter.get();
}
public IHttpFileNameAdapter getFileNameAdapter() {
return fileNameAdapter == null ? null : fileNameAdapter.get();
}
/**
* 如果是匿名内部类完成后需要将adapter设置为空否则会出现内存泄漏
*/
public void setFileLenAdapter(IHttpFileLenAdapter fileLenAdapter) {
this.fileLenAdapter = new SoftReference<>(fileLenAdapter);
}
public void setFileNameAdapter(IHttpFileNameAdapter fileNameAdapter) {
this.fileNameAdapter = new SoftReference<>(fileNameAdapter);
}
public Map<String, String> getFormFields() {
return formFields;
}
public void setFormFields(Map<String, String> formFields) {
this.formFields = formFields;
}
public String getAttachment() {
return TextUtils.isEmpty(attachment) ? "file" : attachment;
}
public void setAttachment(String attachment) {
this.attachment = attachment;
}
public boolean isChunked() {
return isChunked;
}
public void setChunked(boolean chunked) {
isChunked = chunked;
}
public CookieManager getCookieManager() {
return cookieManager;
}
public void setCookieManager(CookieManager cookieManager) {
this.cookieManager = cookieManager;
}
public Proxy getProxy() {
return proxy;
}
public void setProxy(Proxy proxy) {
this.proxy = proxy;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public String getCharSet() {
return TextUtils.isEmpty(charSet) ? "utf-8" : charSet;
}
public void setCharSet(String charSet) {
this.charSet = charSet;
}
public RequestEnum getRequestEnum() {
return requestEnum;
}
public void setRequestEnum(RequestEnum requestEnum) {
this.requestEnum = requestEnum;
}
public boolean isUseServerFileName() {
return useServerFileName;
}
public void setUseServerFileName(boolean useServerFileName) {
this.useServerFileName = useServerFileName;
}
public String getRedirectUrl() {
return redirectUrl;
}
public void setRedirectUrl(String redirectUrl) {
this.redirectUrl = redirectUrl;
}
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
}

View File

@ -0,0 +1,420 @@
/*
* 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.http.download;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.loader.ILoaderVisitor;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.RecordUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* 下载文件信息获取
*/
final class HttpDFileInfoTask implements IInfoTask, Runnable {
private static final String TAG = "HttpDFileInfoTask";
private DownloadEntity mEntity;
private DTaskWrapper mTaskWrapper;
private int mConnectTimeOut;
private Callback callback;
private HttpTaskOption taskOption;
private boolean isStop = false, isCancel = false;
HttpDFileInfoTask(DTaskWrapper taskWrapper) {
this.mTaskWrapper = taskWrapper;
mEntity = taskWrapper.getEntity();
mConnectTimeOut = AriaConfig.getInstance().getDConfig().getConnectTimeOut();
taskOption = (HttpTaskOption) taskWrapper.getTaskOption();
}
@Override public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
TrafficStats.setThreadStatsTag(UUID.randomUUID().toString().hashCode());
HttpURLConnection conn = null;
try {
URL url = ConnectionHelp.handleUrl(mEntity.getUrl(), taskOption);
conn = ConnectionHelp.handleConnection(url, taskOption);
ConnectionHelp.setConnectParam(taskOption, conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()) {
ALog.d(TAG, "head请求");
conn.setRequestMethod("HEAD");
}
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
handleConnect(conn);
} catch (IOException e) {
failDownload(new AriaHTTPException(
String.format("下载失败filePath: %s, url: %s", mEntity.getFilePath(), mEntity.getUrl()),
e), true);
} finally {
if (conn != null) {
try {
InputStream is = conn.getInputStream();
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
conn.disconnect();
}
}
}
@Override public void setCallback(Callback callback) {
this.callback = callback;
}
@Override public void stop() {
isStop = true;
}
@Override public void cancel() {
isCancel = true;
}
private void handleConnect(HttpURLConnection conn) throws IOException {
if (taskOption.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = taskOption.getParams();
if (params != null) {
OutputStreamWriter dos = new OutputStreamWriter(conn.getOutputStream());
Set<String> keys = params.keySet();
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key).append("=").append(URLEncoder.encode(params.get(key))).append("&");
}
String url = sb.toString();
url = url.substring(0, url.length() - 1);
dos.write(url);
dos.flush();
dos.close();
}
}
IHttpFileLenAdapter lenAdapter = taskOption.getFileLenAdapter();
if (lenAdapter == null) {
lenAdapter = new FileLenAdapter();
} else {
ALog.d(TAG, "使用自定义adapter");
}
long len = lenAdapter.handleFileLen(conn.getHeaderFields());
if (!FileUtil.checkMemorySpace(mEntity.getFilePath(), len)) {
failDownload(new AriaHTTPException(
String.format("下载失败内存空间不足filePath: %s, url: %s", mEntity.getFilePath(),
mEntity.getUrl())), false);
return;
}
int code = conn.getResponseCode();
boolean end = false;
if (TextUtils.isEmpty(mEntity.getMd5Code())) {
String md5Code = conn.getHeaderField("Content-MD5");
mEntity.setMd5Code(md5Code);
}
boolean isChunked = false;
final String str = conn.getHeaderField("Transfer-Encoding");
if (!TextUtils.isEmpty(str) && str.equals("chunked")) {
isChunked = true;
}
Map<String, List<String>> headers = conn.getHeaderFields();
String disposition = conn.getHeaderField("Content-Disposition");
if (taskOption.isUseServerFileName()) {
if (!TextUtils.isEmpty(disposition)) {
mEntity.setDisposition(CommonUtil.encryptBASE64(disposition));
handleContentDisposition(disposition);
} else {
if (taskOption.getFileNameAdapter() != null) {
String newName =
taskOption.getFileNameAdapter().handleFileName(headers, mEntity.getKey());
mEntity.setServerFileName(newName);
renameFile(newName);
}
}
}
CookieManager msCookieManager = new CookieManager();
List<String> cookiesHeader = headers.get("Set-Cookie");
if (cookiesHeader != null) {
for (String cookie : cookiesHeader) {
msCookieManager.getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
}
taskOption.setCookieManager(msCookieManager);
}
mTaskWrapper.setCode(code);
if (code == HttpURLConnection.HTTP_PARTIAL) {
if (!checkLen(len) && !isChunked) {
if (len < 0) {
failDownload(
new AriaHTTPException(String.format("任务下载失败文件长度小于0 url: %s", mEntity.getUrl())),
false);
}
return;
}
mEntity.setFileSize(len);
mTaskWrapper.setSupportBP(true);
end = true;
} else if (code == HttpURLConnection.HTTP_OK) {
String contentType = conn.getHeaderField("Content-Type");
if (TextUtils.isEmpty(contentType)) {
return;
}
if (contentType.equals("text/html")) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(ConnectionHelp.convertInputStream(conn)));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
handleUrlReTurn(conn, CommonUtil.getWindowReplaceUrl(sb.toString()));
return;
} else if (!checkLen(len) && !isChunked) {
if (len < 0) {
failDownload(
new AriaHTTPException(
String.format("任务下载失败文件长度小于0 url: %s", mEntity.getUrl())),
false);
}
ALog.d(TAG, "len < 0");
return;
}
mEntity.setFileSize(len);
mTaskWrapper.setNewTask(true);
mTaskWrapper.setSupportBP(false);
end = true;
} else if (code == HttpURLConnection.HTTP_MOVED_TEMP
|| code == HttpURLConnection.HTTP_MOVED_PERM
|| code == HttpURLConnection.HTTP_SEE_OTHER
|| code == HttpURLConnection.HTTP_CREATED // 201 跳转
|| code == 307) {
handleUrlReTurn(conn, conn.getHeaderField("Location"));
} else if (code == 416) { // 处理0k长度的文件的情况
ALog.w(TAG, "文件长度为0不支持断点");
mTaskWrapper.setSupportBP(false);
mTaskWrapper.setNewTask(true);
end = true;
} else if (code >= HttpURLConnection.HTTP_BAD_REQUEST) {
failDownload(new AriaHTTPException(
String.format("任务下载失败errorCode%s, url: %s", code, mEntity.getUrl())), false);
} else {
failDownload(new AriaHTTPException(
String.format("任务下载失败errorCode%s, errorMsg: %s, url: %s", code,
conn.getResponseMessage(), mEntity.getUrl())), !CheckUtil.httpIsBadRequest(code));
}
if (isStop || isCancel) {
return;
}
if (end) {
taskOption.setChunked(isChunked);
CompleteInfo info = new CompleteInfo(code, mTaskWrapper);
callback.onSucceed(mEntity.getUrl(), info);
mEntity.update();
}
}
/**
* 处理"Content-Disposition"参数
* <a href=https://cloud.tencent.com/developer/section/1189916>Content-Disposition</a></>
*
* @throws UnsupportedEncodingException
*/
private void handleContentDisposition(String disposition) throws UnsupportedEncodingException {
if (disposition.contains(";")) {
String[] infos = disposition.split(";");
if (infos[0].equals("attachment")) {
for (String info : infos) {
if (info.startsWith("filename") && info.contains("=")) {
String[] temp = info.split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
break;
}
}
}
} else if (infos[0].equals("form-data") && infos.length > 2) {
String[] temp = infos[2].split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
}
} else {
ALog.w(TAG, "不识别的Content-Disposition参数");
}
}
}
/**
* 重命名文件
*/
private void renameFile(String newName) {
if (TextUtils.isEmpty(newName)) {
ALog.w(TAG, "重命名失败【服务器返回的文件名为空】");
return;
}
ALog.d(TAG, String.format("文件重命名为:%s", newName));
File oldFile = new File(mEntity.getFilePath());
String newPath = oldFile.getParent() + "/" + newName;
if (!CheckUtil.checkDPathConflicts(false, newPath, mTaskWrapper.getRequestType())) {
ALog.e(TAG, "文件重命名失败");
return;
}
if (oldFile.exists()) {
boolean b = oldFile.renameTo(new File(newPath));
ALog.d(TAG, String.format("文件重命名%s", b ? "成功" : "失败"));
}
mEntity.setFileName(newName);
mEntity.setFilePath(newPath);
RecordUtil.modifyTaskRecord(oldFile.getPath(), newPath, mEntity.getTaskType());
}
/**
* 处理30x跳转
*/
private void handleUrlReTurn(HttpURLConnection conn, String newUrl) throws IOException {
ALog.d(TAG, "30x跳转新url为【" + newUrl + "");
if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null")) {
if (callback != null) {
callback.onFail(mEntity, new AriaHTTPException("获取重定向链接失败"), false);
}
return;
}
if (newUrl.startsWith("/")) {
Uri uri = Uri.parse(mEntity.getUrl());
newUrl = uri.getHost() + newUrl;
}
if (!CheckUtil.checkUrl(newUrl)) {
failDownload(new AriaHTTPException("下载失败重定向url错误"), false);
return;
}
taskOption.setRedirectUrl(newUrl);
mEntity.setRedirect(true);
mEntity.setRedirectUrl(newUrl);
String cookies = conn.getHeaderField("Set-Cookie");
conn.disconnect();
URL url = ConnectionHelp.handleUrl(newUrl, taskOption);
conn = ConnectionHelp.handleConnection(url, taskOption);
ConnectionHelp.setConnectParam(taskOption, conn);
conn.setRequestProperty("Cookie", cookies);
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
if (AriaConfig.getInstance().getDConfig().isUseHeadRequest()) {
conn.setRequestMethod("HEAD");
}
conn.setConnectTimeout(mConnectTimeOut);
conn.connect();
handleConnect(conn);
conn.disconnect();
}
/**
* 检查长度是否合法,并且检查新获取的文件长度是否和数据库的文件长度一直,如果不一致,则表示该任务为新任务
*
* @param len 从服务器获取的文件长度
* @return {@code true}合法
*/
private boolean checkLen(long len) {
if (len != mEntity.getFileSize()) {
ALog.d(TAG, "长度不一致,任务为新任务");
mTaskWrapper.setNewTask(true);
}
return true;
}
private void failDownload(AriaHTTPException e, boolean needRetry) {
if (isStop || isCancel) {
return;
}
if (callback != null) {
callback.onFail(mEntity, e, needRetry);
}
}
@Override public void accept(ILoaderVisitor visitor) {
visitor.addComponent(this);
}
private static class FileLenAdapter implements IHttpFileLenAdapter {
@Override public long handleFileLen(Map<String, List<String>> headers) {
if (headers == null || headers.isEmpty()) {
ALog.e(TAG, "header为空获取文件长度失败");
return -1;
}
List<String> sLength = headers.get("Content-Length");
if (sLength == null || sLength.isEmpty()) {
return -1;
}
String temp = sLength.get(0);
long len = TextUtils.isEmpty(temp) ? -1 : Long.parseLong(temp);
// 某些服务如果设置了conn.setRequestProperty("Range", "bytes=" + 0 + "-");
// 会返回 Content-Range: bytes 0-225427911/225427913
if (len < 0) {
List<String> sRange = headers.get("Content-Range");
if (sRange == null || sRange.isEmpty()) {
len = -1;
} else {
int start = temp.indexOf("/");
len = Long.parseLong(temp.substring(start + 1));
}
}
return len;
}
}
}

View File

@ -0,0 +1,221 @@
/*
* 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.http.download;
import com.arialyy.aria.core.common.AbsEntity;
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.loader.IInfoTask;
import com.arialyy.aria.core.loader.ILoaderVisitor;
import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 组合任务文件信息,用于获取长度未知时,组合任务的长度
*/
public final class HttpDGInfoTask implements IInfoTask {
private String TAG = CommonUtil.getClassName(this);
private Callback callback;
private DGTaskWrapper wrapper;
private final Object LOCK = new Object();
private ExecutorService mPool = null;
private boolean getLenComplete = false;
private AtomicInteger count = new AtomicInteger();
private AtomicInteger failCount = new AtomicInteger();
private boolean isStop = false, isCancel = false;
public interface DGInfoCallback extends Callback {
/**
* 子任务失败
*/
void onSubFail(DownloadEntity subEntity, AriaHTTPException e, boolean needRetry);
/**
* 组合任务停止
*/
void onStop(long len);
}
/**
* 子任务回调
*/
private Callback subCallback = new Callback() {
@Override public void onSucceed(String url, CompleteInfo info) {
count.getAndIncrement();
checkGetSizeComplete(count.get(), failCount.get());
ALog.d(TAG, "获取子任务信息完成");
}
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
ALog.e(TAG, String.format("获取文件信息失败url%s", ((DownloadEntity) entity).getUrl()));
count.getAndIncrement();
failCount.getAndIncrement();
((DGInfoCallback) callback).onSubFail((DownloadEntity) entity, new AriaHTTPException(
String.format("子任务获取文件长度失败url%s", ((DownloadEntity) entity).getUrl())), needRetry);
checkGetSizeComplete(count.get(), failCount.get());
}
};
HttpDGInfoTask(DGTaskWrapper wrapper) {
this.wrapper = wrapper;
}
/**
* 停止
*/
@Override
public void stop() {
isStop = true;
if (mPool != null) {
mPool.shutdown();
}
}
@Override public void cancel() {
isCancel = true;
if (mPool != null) {
mPool.shutdown();
}
}
@Override public void run() {
// 如果是isUnknownSize()标志并且获取大小没有完成则直接回调onStop
if (mPool != null && !getLenComplete) {
ALog.d(TAG, "获取长度未完成的情况下,停止组合任务");
mPool.shutdown();
((DGInfoCallback)callback).onStop(0);
return;
}
// 处理组合任务大小未知的情况
if (wrapper.isUnknownSize()) {
mPool = Executors.newCachedThreadPool();
getGroupSize();
try {
synchronized (LOCK) {
LOCK.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!mPool.isShutdown()) {
mPool.shutdown();
}
} else {
for (DTaskWrapper wrapper : wrapper.getSubTaskWrapper()) {
cloneHeader(wrapper);
}
callback.onSucceed(wrapper.getKey(), new CompleteInfo());
}
}
/*
* 获取组合任务大小,使用该方式获取到的组合任务大小,子任务不需要再重新获取文件大小
*/
private void getGroupSize() {
new Thread(new Runnable() {
@Override public void run() {
for (DTaskWrapper dTaskWrapper : wrapper.getSubTaskWrapper()) {
DownloadEntity subEntity = dTaskWrapper.getEntity();
if (subEntity.getFileSize() > 0) {
count.getAndIncrement();
if (subEntity.getCurrentProgress() < subEntity.getFileSize()) {
// 如果没有完成需要拷贝一份数据
cloneHeader(dTaskWrapper);
}
checkGetSizeComplete(count.get(), failCount.get());
continue;
}
cloneHeader(dTaskWrapper);
HttpDFileInfoTask infoTask = new HttpDFileInfoTask(dTaskWrapper);
infoTask.setCallback(subCallback);
mPool.execute(infoTask);
}
}
}).start();
}
/**
* 检查组合任务大小是否获取完成,获取完成后取消阻塞,并设置组合任务大小
*/
private void checkGetSizeComplete(int count, int failCount) {
if (isStop || isCancel) {
ALog.w(TAG, "任务已停止或已取消isStop = " + isStop + ", isCancel = " + isCancel);
notifyLock();
return;
}
if (failCount == wrapper.getSubTaskWrapper().size()) {
callback.onFail(wrapper.getEntity(), new AriaHTTPException("获取子任务长度失败"), false);
notifyLock();
return;
}
if (count == wrapper.getSubTaskWrapper().size()) {
long size = 0;
for (DTaskWrapper wrapper : wrapper.getSubTaskWrapper()) {
size += wrapper.getEntity().getFileSize();
}
wrapper.getEntity().setConvertFileSize(CommonUtil.formatFileSize(size));
wrapper.getEntity().setFileSize(size);
wrapper.getEntity().update();
getLenComplete = true;
ALog.d(TAG, String.format("获取组合任务长度完成,组合任务总长度:%s失败的子任务数%s", size, failCount));
callback.onSucceed(wrapper.getKey(), new CompleteInfo());
notifyLock();
}
}
private void notifyLock() {
synchronized (LOCK) {
LOCK.notifyAll();
}
}
/**
* 子任务使用父包裹器的属性
*/
private void cloneHeader(DTaskWrapper taskWrapper) {
HttpTaskOption groupOption = (HttpTaskOption) wrapper.getTaskOption();
HttpTaskOption subOption = new HttpTaskOption();
// 设置属性
subOption.setFileLenAdapter(groupOption.getFileLenAdapter());
subOption.setFileNameAdapter(groupOption.getFileNameAdapter());
subOption.setUseServerFileName(groupOption.isUseServerFileName());
subOption.setFileNameAdapter(groupOption.getFileNameAdapter());
subOption.setRequestEnum(groupOption.getRequestEnum());
subOption.setHeaders(groupOption.getHeaders());
subOption.setProxy(groupOption.getProxy());
subOption.setParams(groupOption.getParams());
taskWrapper.setTaskOption(subOption);
}
@Override public void setCallback(Callback callback) {
this.callback = callback;
}
@Override public void accept(ILoaderVisitor visitor) {
visitor.addComponent(this);
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.http.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.download.DownloadEntity;
import com.arialyy.aria.core.group.AbsGroupLoader;
import com.arialyy.aria.core.group.AbsSubDLoadUtil;
import com.arialyy.aria.core.listener.DownloadGroupListener;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.exception.AriaHTTPException;
import java.io.File;
/**
* http 组合任务加载器
*/
final class HttpDGLoader extends AbsGroupLoader {
HttpDGLoader(AbsTaskWrapper groupWrapper, DownloadGroupListener listener) {
super(groupWrapper, listener);
}
@Override protected void handlerTask(Looper looper) {
if (isBreak()) {
return;
}
mInfoTask.run();
}
@Override
protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) {
HttpSubDLoaderUtil subUtil = new HttpSubDLoaderUtil(getScheduler(), needGetFileInfo, getKey());
subUtil.setParams(wrapper, null);
return subUtil;
}
private void startSub() {
if (isBreak()) {
return;
}
onPostStart();
for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) {
DownloadEntity dEntity = wrapper.getEntity();
startSubLoader(createSubLoader(wrapper, dEntity.getFileSize() < 0));
}
}
@Override public void addComponent(IInfoTask infoTask) {
mInfoTask = infoTask;
mInfoTask.setCallback(new HttpDGInfoTask.DGInfoCallback() {
@Override
public void onSubFail(DownloadEntity subEntity, AriaHTTPException e, boolean needRetry) {
getState().countFailNum(subEntity.getKey());
}
@Override public void onStop(long len) {
getListener().onStop(len);
}
@Override public void onSucceed(String key, CompleteInfo info) {
startSub();
}
@Override public void onFail(AbsEntity entity, AriaException e, boolean needRetry) {
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.http.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.listener.DownloadGroupListener;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.http.HttpTaskOption;
/**
* Created by AriaL on 2017/6/30.
* 任务组下载工具
*/
public final class HttpDGLoaderUtil extends AbsGroupLoaderUtil {
@Override protected AbsGroupLoader getLoader() {
if (mLoader == null) {
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
mLoader = new HttpDGLoader(getTaskWrapper(), (DownloadGroupListener) getListener());
}
return mLoader;
}
@Override protected LoaderStructure buildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new HttpDGInfoTask((DGTaskWrapper) getTaskWrapper()));
structure.accept(getLoader());
return structure;
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.http.download;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.AbsNormalLoader;
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.loader.NormalLoader;
import com.arialyy.aria.core.loader.NormalTTBuilder;
import com.arialyy.aria.core.loader.NormalThreadStateManager;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.http.HttpRecordHandler;
import com.arialyy.aria.http.HttpTaskOption;
/**
* @author lyy
* Date: 2019-09-21
*/
public final class HttpDLoaderUtil extends AbsNormalLoaderUtil {
@Override public AbsNormalLoader getLoader() {
if (mLoader == null){
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
mLoader = new NormalLoader(getTaskWrapper(), getListener());
}
return mLoader;
}
public LoaderStructure BuildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new HttpRecordHandler(getTaskWrapper()))
.addComponent(new NormalThreadStateManager(getListener()))
.addComponent(new HttpDFileInfoTask((DTaskWrapper) getTaskWrapper()))
.addComponent(new NormalTTBuilder(getTaskWrapper(), new HttpDTTBuilderAdapter()));
structure.accept(getLoader());
return structure;
}
}

View File

@ -0,0 +1,61 @@
package com.arialyy.aria.http.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.BufferedRandomAccessFile;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
import java.io.IOException;
final class HttpDTTBuilderAdapter extends AbsNormalTTBuilderAdapter {
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {
return new HttpDThreadTaskAdapter(config);
}
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
if (!record.isBlock) {
if (getTempFile().exists()) {
FileUtil.deleteFile(getTempFile());
}
} 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);
}
}
}
BufferedRandomAccessFile file = null;
try {
if (totalThreadNum > 1 && !record.isBlock) {
file = new BufferedRandomAccessFile(getTempFile().getPath(), "rwd", 8192);
//设置文件长度
file.setLength(getEntity().getFileSize());
}
if (getTempFile().exists()) {
FileUtil.deleteFile(getTempFile());
}
return true;
} catch (IOException e) {
e.printStackTrace();
ALog.e(TAG, String.format("下载失败filePath: %s, url: %s", getEntity().getFilePath(),
getEntity().getUrl()));
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
}

View File

@ -0,0 +1,275 @@
/*
* 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.http.download;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.BaseHttpThreadTaskAdapter;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Map;
import java.util.Set;
/**
* Created by lyy on 2017/1/18. 下载线程
*/
final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
private final String TAG = "HttpDThreadTaskAdapter";
private DTaskWrapper mTaskWrapper;
HttpDThreadTaskAdapter(SubThreadConfig config) {
super(config);
}
@Override protected void handlerThreadTask() {
mTaskWrapper = (DTaskWrapper) getTaskWrapper();
if (getThreadRecord().isComplete) {
handleComplete();
return;
}
HttpURLConnection conn = null;
BufferedInputStream is = null;
BufferedRandomAccessFile file = null;
try {
URL url = ConnectionHelp.handleUrl(getThreadConfig().url, mTaskOption);
conn = ConnectionHelp.handleConnection(url, mTaskOption);
if (mTaskWrapper.isSupportBP()) {
ALog.d(TAG,
String.format("任务【%s】线程__%s__开始下载【开始位置 : %s结束位置%s】", getFileName(),
getThreadRecord().threadId, getThreadRecord().startLocation,
getThreadRecord().endLocation));
conn.setRequestProperty("Range",
String.format("bytes=%s-%s", getThreadRecord().startLocation,
(getThreadRecord().endLocation - 1)));
} else {
ALog.w(TAG, "该下载不支持断点");
}
ConnectionHelp.setConnectParam(mTaskOption, conn);
conn.setConnectTimeout(getTaskConfig().getConnectTimeOut());
conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数
if (mTaskOption.isChunked()) {
conn.setDoInput(true);
conn.setChunkedStreamingMode(0);
}
conn.connect();
// 传递参数
if (mTaskOption.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = mTaskOption.getParams();
if (params != null) {
OutputStreamWriter dos = new OutputStreamWriter(conn.getOutputStream());
Set<String> keys = params.keySet();
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key).append("=").append(URLEncoder.encode(params.get(key))).append("&");
}
String paramStr = sb.toString();
paramStr = paramStr.substring(0, paramStr.length() - 1);
dos.write(paramStr);
dos.flush();
dos.close();
}
}
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (mTaskOption.isChunked()) {
readChunked(is);
} else if (getThreadConfig().isBlock) {
readDynamicFile(is);
} else {
//创建可设置位置的文件
file =
new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd",
getTaskConfig().getBuffSize());
//设置每条线程写入文件的位置
if (getThreadRecord().startLocation > 0) {
file.seek(getThreadRecord().startLocation);
}
readNormal(is, file);
handleComplete();
}
} catch (MalformedURLException e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} catch (IOException e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), true);
} catch (ArrayIndexOutOfBoundsException e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} catch (Exception e) {
fail(new AriaHTTPException(String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} finally {
try {
if (file != null) {
file.close();
}
if (is != null) {
is.close();
}
if (conn != null) {
conn.getInputStream().close();
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取chunked数据
*/
private void readChunked(InputStream is) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(getThreadConfig().tempFile, true);
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);
}
fos.write(buffer, 0, len);
progress(len);
}
handleComplete();
} catch (IOException e) {
fail(new AriaHTTPException(
String.format("文件下载失败savePath: %s, url: %s", getEntity().getFilePath(),
getThreadConfig().url), e), true);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 动态长度文件读取方式
*/
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());
//如果要通过 Future 的 cancel 方法取消正在运行的任务,那么该任务必定是可以 对线程中断做出响应 的任务。
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 AriaHTTPException(String.format("文件下载失败savePath: %s, url: %s", getEntity().getFilePath(),
getThreadConfig().url), e), true);
} finally {
try {
if (fos != null) {
fos.flush();
fos.close();
}
if (foc != null) {
foc.close();
}
if (fic != null) {
fic.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取普通的文件流
*/
private void readNormal(InputStream is, BufferedRandomAccessFile file)
throws IOException {
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);
}
file.write(buffer, 0, len);
progress(len);
}
}
/**
* 处理完成配置文件的更新或事件回调
*/
private void handleComplete() {
if (getThreadTask().isBreak()) {
return;
}
if (!getThreadTask().checkBlock()) {
return;
}
complete();
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.http.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 HttpSubDLoaderUtil extends AbsSubDLoadUtil {
/**
* @param schedulers 调度器
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息
*/
HttpSubDLoaderUtil( 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 HttpDTTBuilderAdapter()))
.addComponent(new HttpDFileInfoTask(getWrapper()));
structure.accept(getLoader());
return structure;
}
}

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.http.upload;
import android.os.Handler;
import android.os.Looper;
import com.arialyy.aria.core.inf.IThreadStateManager;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.AbsNormalLoader;
import com.arialyy.aria.core.loader.IInfoTask;
import com.arialyy.aria.core.loader.IRecordHandler;
import com.arialyy.aria.core.loader.IThreadTaskBuilder;
import com.arialyy.aria.core.manager.ThreadTaskManager;
import com.arialyy.aria.core.task.IThreadTask;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.util.ALog;
import java.util.List;
final class HttpULoader extends AbsNormalLoader<UTaskWrapper> {
HttpULoader(UTaskWrapper wrapper, IEventListener listener) {
super(wrapper, listener);
}
@Override public void addComponent(IRecordHandler recordHandler) {
mRecordHandler = recordHandler;
}
/**
* @deprecated http 上传任务不需要设置这个
*/
@Deprecated
@Override public void addComponent(IInfoTask infoTask) {
}
@Override public void addComponent(IThreadStateManager threadState) {
mStateManager = threadState;
}
@Override public void addComponent(IThreadTaskBuilder builder) {
mTTBuilder = builder;
}
@Override protected void handleTask(Looper looper) {
mRecord = mRecordHandler.getRecord(getFileSize());
mStateManager.setLooper(mRecord, looper);
List<IThreadTask> tt = mTTBuilder.buildThreadTask(mRecord,
new Handler(looper, mStateManager.getHandlerCallback()));
if (tt == null || tt.isEmpty()) {
ALog.e(TAG, "创建线程任务失败");
getListener().onFail(false, new AriaHTTPException("创建线程任务失败"));
return;
}
getListener().onStart(0);
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), tt.get(0));
startTimer();
}
@Override public long getFileSize() {
return mTaskWrapper.getEntity().getFileSize();
}
@Override protected void checkComponent() {
if (mRecordHandler == null) {
throw new NullPointerException("任务记录组件为空");
}
if (mStateManager == null) {
throw new NullPointerException("任务状态管理组件为空");
}
if (mTTBuilder == null) {
throw new NullPointerException("线程任务组件为空");
}
}
@Override public long getCurrentProgress() {
return mStateManager.getCurrentProgress();
}
}

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.http.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.http.HttpRecordHandler;
import com.arialyy.aria.http.HttpTaskOption;
/**
* @author lyy
* Date: 2019-09-19
*/
public final class HttpULoaderUtil extends AbsNormalLoaderUtil {
@Override public AbsNormalLoader getLoader() {
if (mLoader == null) {
getTaskWrapper().generateTaskOption(HttpTaskOption.class);
mLoader = new HttpULoader((UTaskWrapper) getTaskWrapper(), getListener());
}
return mLoader;
}
@Override public LoaderStructure BuildLoaderStructure() {
LoaderStructure structure = new LoaderStructure();
structure.addComponent(new HttpRecordHandler(getTaskWrapper()))
// .addComponent(new NormalThreadStateManager(getListener()))
.addComponent(new UploadThreadStateManager(getListener()))
.addComponent(new NormalTTBuilder(getTaskWrapper(), new AbsNormalTTBuilderAdapter() {
@Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) {
return new HttpUThreadTaskAdapter(config);
}
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
return true;
}
}));
structure.accept(getLoader());
return structure;
}
}

View File

@ -0,0 +1,235 @@
/*
* 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.http.upload;
import android.text.TextUtils;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.exception.AriaHTTPException;
import com.arialyy.aria.http.BaseHttpThreadTaskAdapter;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Created by Aria.Lao on 2017/7/28. 不支持断点的HTTP上传任务
*/
final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
private final String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
private final String PREFIX = "--", LINE_END = "\r\n";
private HttpURLConnection mHttpConn;
private OutputStream mOutputStream;
HttpUThreadTaskAdapter(SubThreadConfig config) {
super(config);
}
@Override protected void handlerThreadTask() {
File uploadFile = new File(getEntity().getFilePath());
if (!uploadFile.exists()) {
fail(new AriaHTTPException(
String.format("上传失败文件不存在filePath: %s, url: %s", getEntity().getFilePath(),
getEntity().getUrl())));
return;
}
URL url;
try {
url = new URL(CommonUtil.convertUrl(getThreadConfig().url));
mHttpConn = (HttpURLConnection) url.openConnection();
mHttpConn.setRequestMethod(mTaskOption.getRequestEnum().name);
mHttpConn.setUseCaches(false);
mHttpConn.setDoOutput(true);
mHttpConn.setDoInput(true);
mHttpConn.setRequestProperty("Connection", "Keep-Alive");
mHttpConn.setRequestProperty("Content-Type",
getContentType() + "; boundary=" + BOUNDARY);
mHttpConn.setRequestProperty("User-Agent", getUserAgent());
mHttpConn.setConnectTimeout(getTaskConfig().getConnectTimeOut());
mHttpConn.setReadTimeout(getTaskConfig().getIOTimeOut());
//内部缓冲区---分段上传防止oom
mHttpConn.setChunkedStreamingMode(getTaskConfig().getBuffSize());
//添加Http请求头部
Set<String> keys = mTaskOption.getHeaders().keySet();
for (String key : keys) {
mHttpConn.setRequestProperty(key, mTaskOption.getHeaders().get(key));
}
mOutputStream = mHttpConn.getOutputStream();
PrintWriter writer =
new PrintWriter(new OutputStreamWriter(mOutputStream, mTaskOption.getCharSet()), true);
// 添加参数
Map<String, String> params = mTaskOption.getParams();
if (params != null && !params.isEmpty()) {
for (String key : params.keySet()) {
addFormField(writer, key, params.get(key));
}
}
//添加文件上传表单字段
keys = mTaskOption.getFormFields().keySet();
for (String key : keys) {
addFormField(writer, key, mTaskOption.getFormFields().get(key));
}
uploadFile(writer, mTaskOption.getAttachment(), uploadFile);
finish(writer);
} catch (Exception e) {
e.printStackTrace();
fail(new AriaHTTPException(
String.format("上传失败filePath: %s, url: %s", getEntity().getFilePath(),
getEntity().getUrl()), e));
}
}
private void fail(AriaHTTPException e1) {
try {
fail(e1, false);
if (mOutputStream != null) {
mOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String getContentType() {
//return (mTaskOption.getHeaders() == null || TextUtils.isEmpty(
// mTaskOption.getHeaders().get("Content-Type"))) ? "multipart/form-data"
// : mTaskOption.getHeaders().get("Content-Type");
return "multipart/form-data";
}
private String getUserAgent() {
return (mTaskOption.getHeaders() == null || TextUtils.isEmpty(
mTaskOption.getHeaders().get("User-Agent")))
? "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)"
: mTaskOption.getHeaders().get("User-Agent");
}
/**
* 添加文件上传表单字段
*/
private void addFormField(PrintWriter writer, String name, String value) {
writer.append(PREFIX).append(BOUNDARY).append(LINE_END);
writer.append("Content-Disposition: form-data; name=\"")
.append(name)
.append("\"")
.append(LINE_END);
writer.append("Content-Type: text/plain; charset=")
.append(mTaskOption.getCharSet())
.append(LINE_END);
writer.append(LINE_END);
writer.append(value).append(LINE_END);
writer.flush();
}
/**
* 上传文件
*
* @param attachment 文件上传attachment
* @throws IOException
*/
private void uploadFile(PrintWriter writer, String attachment, File uploadFile)
throws IOException {
writer.append(PREFIX).append(BOUNDARY).append(LINE_END);
writer.append("Content-Disposition: form-data; name=\"")
.append(attachment)
.append("\"; filename=\"")
.append(getEntity().getFileName())
.append("\"")
.append(LINE_END);
writer.append("Content-Type: ")
.append(URLConnection.guessContentTypeFromName(getEntity().getFileName()))
.append(LINE_END);
writer.append("Content-Transfer-Encoding: binary").append(LINE_END);
writer.append(LINE_END);
writer.flush();
FileInputStream inputStream = new FileInputStream(uploadFile);
byte[] buffer = new byte[4096];
int bytesLen;
while ((bytesLen = inputStream.read(buffer)) != -1) {
mOutputStream.write(buffer, 0, bytesLen);
progress(bytesLen);
if (getThreadTask().isBreak()) {
break;
}
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(bytesLen);
}
}
mOutputStream.flush();
inputStream.close();
writer.append(LINE_END).flush();
// 保证上传的文件和本地的一致https://www.cnblogs.com/tugenhua0707/p/8975121.html
writer.append(PREFIX).append(BOUNDARY).append(PREFIX).append(LINE_END).flush();
}
/**
* 任务结束操作
*
* @throws IOException
*/
private String finish(PrintWriter writer) throws IOException {
StringBuilder response = new StringBuilder();
int status = mHttpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(ConnectionHelp.convertInputStream(mHttpConn)));
String line;
while (getThreadTask().isLive() && (line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
mHttpConn.disconnect();
getEntity().setResponseStr(response.toString());
complete();
} else {
String msg = "response msg: " + mHttpConn.getResponseMessage() + "code: " + status;
ALog.e(TAG, msg);
getEntity().setResponseStr(response.toString());
fail(new AriaHTTPException(msg), false);
response.append(status);
}
writer.flush();
writer.close();
mOutputStream.close();
return response.toString();
}
@Override protected UploadEntity getEntity() {
return (UploadEntity) super.getEntity();
}
}

View File

@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@ -0,0 +1,3 @@
com.arialyy.aria.http.download.HttpDLoaderUtil
com.arialyy.aria.http.download.HttpDGLoaderUtil
com.arialyy.aria.http.upload.HttpULoaderUtil