parent
							
								
									49ccc1b28a
								
							
						
					
					
						commit
						16e76b42ed
					
				| @ -1,41 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package com.arialyy.aria.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(); | ||||
|   } | ||||
| } | ||||
| @ -1,184 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package com.arialyy.aria.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.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.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, HttpOption option) | ||||
|       throws MalformedURLException { | ||||
|     Map<String, String> params = option.getParams(); | ||||
|     if (params.isEmpty()) { | ||||
|       return new URL(CommonUtil.convertUrl(url)); | ||||
|     } | ||||
| 
 | ||||
|     if (option.getRequestMethod() == RequestEnum.GET) { | ||||
|       if (!url.contains("?")) { | ||||
|         url = url.concat("?"); | ||||
|       } | ||||
|       StringBuilder sb = new StringBuilder(); | ||||
|       sb.append(url).append("?"); | ||||
|       Set<String> keys = params.keySet(); | ||||
|       for (String key : keys) { | ||||
|         sb.append(URLEncoder.encode(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)); | ||||
|     } | ||||
| 
 | ||||
|     if (option.getRequestMethod() == RequestEnum.POST) { | ||||
| 
 | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 处理链接 | ||||
|    * | ||||
|    * @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; | ||||
|   } | ||||
| } | ||||
| @ -1,111 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package com.arialyy.aria.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; | ||||
|   } | ||||
| } | ||||
| @ -1,191 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.arialyy.aria.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; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -1,31 +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.upload | ||||
| 
 | ||||
| import com.arialyy.aria.core.task.ITaskInterceptor | ||||
| import com.arialyy.aria.core.task.TaskChain | ||||
| import com.arialyy.aria.core.task.TaskResp | ||||
| 
 | ||||
| /** | ||||
|  * @Author laoyuyu | ||||
|  * @Description | ||||
|  * @Date 9:47 PM 2023/2/21 | ||||
|  **/ | ||||
| class HttpUBlockInterceptor: ITaskInterceptor { | ||||
|   override suspend fun interceptor(chain: TaskChain): TaskResp { | ||||
| 
 | ||||
|   } | ||||
| } | ||||
| ///* | ||||
| // * 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.inf.IBlockManager | ||||
| //import com.arialyy.aria.core.task.ITask | ||||
| //import com.arialyy.aria.core.task.ITaskInterceptor | ||||
| //import com.arialyy.aria.core.task.TaskChain | ||||
| //import com.arialyy.aria.core.task.TaskResp | ||||
| //import com.arialyy.aria.http.HttpTaskOption | ||||
| //import com.arialyy.aria.orm.entity.TaskRecord | ||||
| // | ||||
| ///** | ||||
| // * @Author laoyuyu | ||||
| // * @Description | ||||
| // * @Date 9:47 PM 2023/2/21 | ||||
| // **/ | ||||
| //class HttpUBlockInterceptor: ITaskInterceptor { | ||||
| // | ||||
| //  private lateinit var task: ITask | ||||
| //  private lateinit var option: HttpTaskOption | ||||
| //  private lateinit var blockManager: IBlockManager | ||||
| //  private lateinit var taskRecord: TaskRecord | ||||
| // | ||||
| //  override suspend fun interceptor(chain: TaskChain): TaskResp { | ||||
| // | ||||
| //  } | ||||
| //} | ||||
| @ -1,94 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package com.arialyy.aria.http.upload; | ||||
| 
 | ||||
| import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import com.arialyy.aria.core.inf.IBlockManager; | ||||
| 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 HttpULoader1 extends AbsNormalLoader<UTaskWrapper> { | ||||
|   HttpULoader1(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(IBlockManager 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(); | ||||
|   } | ||||
| } | ||||
| @ -1,62 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package com.arialyy.aria.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.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 HttpULoader1((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; | ||||
|   } | ||||
| } | ||||
| @ -1,235 +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(); | ||||
|   } | ||||
| } | ||||
| ///*
 | ||||
| // * 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();
 | ||||
| //  }
 | ||||
| //}
 | ||||
|  | ||||
| @ -1,275 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
 | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package com.arialyy.aria.core.loader; | ||||
| 
 | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import android.os.Message; | ||||
| 
 | ||||
| import com.arialyy.aria.core.TaskRecord; | ||||
| import com.arialyy.aria.core.inf.IBlockManager; | ||||
| import com.arialyy.aria.core.listener.IEventListener; | ||||
| import com.arialyy.aria.exception.AriaException; | ||||
| import com.arialyy.aria.util.ALog; | ||||
| import com.arialyy.aria.util.CommonUtil; | ||||
| import com.arialyy.aria.util.FileUtil; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.atomic.AtomicInteger; | ||||
| 
 | ||||
| /** | ||||
|  * 线程任务管理器,用于处理多线程下载时任务的状态回调 | ||||
|  */ | ||||
| public class UploadThreadStateManager implements IBlockManager { | ||||
|   private final String TAG = CommonUtil.getClassName(this); | ||||
| 
 | ||||
|   /** | ||||
|    * 任务状态回调 | ||||
|    */ | ||||
|   private IEventListener mListener; | ||||
|   private int mThreadNum;    // 启动的线程总数
 | ||||
|   private AtomicInteger mCancelNum = new AtomicInteger(0); // 已经取消的线程的数
 | ||||
|   private AtomicInteger mStopNum = new AtomicInteger(0);  // 已经停止的线程数
 | ||||
|   private AtomicInteger mFailNum = new AtomicInteger(0);  // 失败的线程数
 | ||||
|   private AtomicInteger mCompleteNum = new AtomicInteger(0);  // 完成的线程数
 | ||||
|   private long mProgress; //当前总进度
 | ||||
|   private TaskRecord mTaskRecord; // 任务记录
 | ||||
|   private Looper mLooper; | ||||
| 
 | ||||
|   /** | ||||
|    * @param listener 任务事件 | ||||
|    */ | ||||
|   public UploadThreadStateManager(IEventListener listener) { | ||||
|     mListener = listener; | ||||
|   } | ||||
| 
 | ||||
|   @Override public void setLooper(TaskRecord taskRecord, Looper looper) { | ||||
|     mTaskRecord = taskRecord; | ||||
|     mThreadNum = mTaskRecord.threadNum; | ||||
|     mLooper = looper; | ||||
|   } | ||||
| 
 | ||||
|   private void checkLooper() { | ||||
|     if (mTaskRecord == null) { | ||||
|       throw new NullPointerException("任务记录为空"); | ||||
|     } | ||||
|     if (mLooper == null) { | ||||
|       throw new NullPointerException("Looper为空"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private Handler.Callback callback = new Handler.Callback() { | ||||
|     @Override public boolean handleMessage(Message msg) { | ||||
|       checkLooper(); | ||||
|       switch (msg.what) { | ||||
|         case STATE_STOP: | ||||
|           mStopNum.getAndIncrement(); | ||||
|           if (isStop()) { | ||||
|             quitLooper(); | ||||
|           } | ||||
|           break; | ||||
|         case STATE_CANCEL: | ||||
|           mCancelNum.getAndIncrement(); | ||||
|           if (isCancel()) { | ||||
|             quitLooper(); | ||||
|           } | ||||
|           break; | ||||
|         case STATE_FAIL: | ||||
|           mFailNum.getAndIncrement(); | ||||
|           if (hasFailedBlock()) { | ||||
|             Bundle b = msg.getData(); | ||||
|             mListener.onFail(b.getBoolean(DATA_RETRY, false), | ||||
|                 (AriaException) b.getSerializable(DATA_ERROR_INFO)); | ||||
|             quitLooper(); | ||||
|           } | ||||
|           break; | ||||
|         case STATE_COMPLETE: | ||||
|           mCompleteNum.getAndIncrement(); | ||||
|           if (isCompleted()) { | ||||
|             ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum); | ||||
|             //上传文件不需要合并文件
 | ||||
|             mListener.onComplete(); | ||||
|             quitLooper(); | ||||
|           } | ||||
|           break; | ||||
|         case STATE_RUNNING: | ||||
|           Bundle b = msg.getData(); | ||||
|           if (b != null) { | ||||
|             long len = b.getLong(IBlockManager.DATA_ADD_LEN, 0); | ||||
|             mProgress += len; | ||||
|           } | ||||
| 
 | ||||
|           break; | ||||
|         case STATE_UPDATE_PROGRESS: | ||||
|           if (msg.obj == null) { | ||||
|             mProgress = updateBlockProgress(); | ||||
|           } else if (msg.obj instanceof Long) { | ||||
|             mProgress = (long) msg.obj; | ||||
|           } | ||||
|           break; | ||||
|       } | ||||
|       return false; | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   @Override public void updateCurrentProgress(long currentProgress) { | ||||
|     mProgress = currentProgress; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 退出looper循环 | ||||
|    */ | ||||
|   private void quitLooper() { | ||||
|     mLooper.quit(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 获取当前任务下载进度 | ||||
|    * | ||||
|    * @return 当前任务下载进度 | ||||
|    */ | ||||
|   @Override | ||||
|   public long getCurrentProgress() { | ||||
|     return mProgress; | ||||
|   } | ||||
| 
 | ||||
|   @Override public Handler.Callback getHandlerCallback() { | ||||
|     return callback; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 所有子线程是否都已经停止 | ||||
|    */ | ||||
|   public boolean isStop() { | ||||
|     //ALog.d(TAG,
 | ||||
|     //    String.format("isStop; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
 | ||||
|     //        mCancelNum, mFailNum, mCompleteNum));
 | ||||
|     return mStopNum.get() == mThreadNum || mStopNum.get() + mCompleteNum.get() == mThreadNum; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 所有子线程是否都已经失败 | ||||
|    */ | ||||
|   @Override | ||||
|   public boolean hasFailedBlock() { | ||||
|     //ALog.d(TAG,
 | ||||
|     //    String.format("isFail; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s", mStopNum,
 | ||||
|     //        mCancelNum, mFailNum, mCompleteNum));
 | ||||
|     return mCompleteNum.get() != mThreadNum | ||||
|         && (mFailNum.get() == mThreadNum || mFailNum.get() + mCompleteNum.get() == mThreadNum); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 所有子线程是否都已经完成 | ||||
|    */ | ||||
|   @Override | ||||
|   public boolean isCompleted() { | ||||
|     //ALog.d(TAG,
 | ||||
|     //    String.format("isComplete; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
 | ||||
|     //        mStopNum,
 | ||||
|     //        mCancelNum, mFailNum, mCompleteNum));
 | ||||
|     return mCompleteNum.get() == mThreadNum; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 所有子线程是否都已经取消 | ||||
|    */ | ||||
|   public boolean isCancel() { | ||||
|     //ALog.d(TAG, String.format("isCancel; stopNum: %s, cancelNum: %s, failNum: %s, completeNum: %s",
 | ||||
|     //    mStopNum,
 | ||||
|     //    mCancelNum, mFailNum, mCompleteNum));
 | ||||
|     return mCancelNum.get() == mThreadNum; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 更新分块任务s的真实进度 | ||||
|    */ | ||||
|   private long updateBlockProgress() { | ||||
|     long size = 0; | ||||
|     for (int i = 0, len = mTaskRecord.threadRecords.size(); i < len; i++) { | ||||
|       File temp = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); | ||||
|       if (temp.exists()) { | ||||
|         size += temp.length(); | ||||
|       } | ||||
|     } | ||||
|     return size; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 合并sftp的分块 | ||||
|    */ | ||||
|   private boolean mergerSFtp() { | ||||
|     if (mTaskRecord.threadNum == 1) { | ||||
|       File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)); | ||||
|       return partFile.renameTo(new File(mTaskRecord.filePath)); | ||||
|     } | ||||
| 
 | ||||
|     List<String> partPath = new ArrayList<>(); | ||||
|     for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) { | ||||
|       partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); | ||||
|     } | ||||
|     FileUtil.mergeSFtpFile(mTaskRecord.filePath, partPath, mTaskRecord.fileLength); | ||||
|     for (String pp : partPath) { | ||||
|       FileUtil.deleteFile(pp); | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 合并文件 | ||||
|    * | ||||
|    * @return {@code true} 合并成功,{@code false}合并失败 | ||||
|    */ | ||||
|   private boolean mergeFile() { | ||||
|     if (mTaskRecord.threadNum == 1) { | ||||
|       File targetFile = new File(mTaskRecord.filePath); | ||||
|       if (targetFile.exists() && targetFile.length() == mTaskRecord.fileLength){ | ||||
|         return true; | ||||
|       } | ||||
|       FileUtil.deleteFile(targetFile); | ||||
|       File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)); | ||||
|       return partFile.renameTo(targetFile); | ||||
|     } | ||||
| 
 | ||||
|     List<String> partPath = new ArrayList<>(); | ||||
|     for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) { | ||||
|       partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); | ||||
|     } | ||||
|     boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath); | ||||
|     if (isSuccess) { | ||||
|       for (String pp : partPath) { | ||||
|         FileUtil.deleteFile(pp); | ||||
|       } | ||||
|       File targetFile = new File(mTaskRecord.filePath); | ||||
|       if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) { | ||||
|         ALog.e(TAG, String.format("任务【%s】分块文件合并失败,下载长度超出文件真实长度,downloadLen: %s,fileSize: %s", | ||||
|             targetFile.getName(), targetFile.length(), mTaskRecord.fileLength)); | ||||
|         return false; | ||||
|       } | ||||
|       return true; | ||||
|     } else { | ||||
|       ALog.e(TAG, "合并失败"); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @Override public void accept(ILoaderVisitor visitor) { | ||||
|     visitor.addComponent(this); | ||||
|   } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue