最近学习研究了下用http进行大文件上传:
经过不断修复优化,功能实现如下:
1.上传大文件;
2.实现多文件同时上传;
3.实现文件断点续传;
4.提供上传回调,显示上传速度与进度;
5.多线程上传,使用线程池进行管理;
6.上传失败保存现场,下回继续上传;
……..
直接贴代码,如有疑问请留言,参与讨论;
有三个核心类:ResumableUploadUtil ,UpLoadFileInfo,UploadTimerTask
ResumableUploadUtil 用于完成上传核心逻辑:
package upload;
import android.os.Handler;
import upload.UpLoadFileInfo;
import upload.ConstantValue;
import upload.MyApplicationLike;
import upload.ActivityStack;
import upload.Logger;
import upload.StringUtil;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.concurrent.Executors;
/**
* Created by LiKang on 2016/12/22 15:15.
* email:15034671952@163.com
*/
public class ResumableUploadUtil {
private final String Tag = "ResumableUploadUtil";
private final String BOUNDARY = "----androidUploadBinBoundary";
private Handler handler;
/**
* 默认分段大小
*/
public final static long defaultChunkSize = 1024 * 1024 * 3 / 2;
/**
* 默认缓冲区大小
*/
public final static int defaultBufferSize = 1024 * 4;
/**
* 默认并发上传线程数
*/
public final static int defalutConcurrentThreadsNum = 3;
private int concurrentThreadsNum = ResumableUploadUtil.defalutConcurrentThreadsNum;
public ResumableUploadUtil() {
this.handler = new Handler();
}
/**
* 上传 准备;
*/
private void upLoadPrepare(UpLoadFileInfo fileInfo) {
computeChunkNums(fileInfo);
computeEachThreadChunkNum(fileInfo);
}
/**
* 计算分几片段;
*/
private void computeChunkNums(UpLoadFileInfo fileInfo) {
long tempLastChunkSize = fileInfo.fileSize % defaultChunkSize;
fileInfo.totalChunks = (int) (fileInfo.fileSize / defaultChunkSize);
if (tempLastChunkSize != 0) {
fileInfo.totalChunks += 1;
}
Logger.d(Tag, "totalChunks:" + fileInfo.totalChunks);
}
/**
* 设置 工作线程信息
*/
private void computeEachThreadChunkNum(UpLoadFileInfo fileInfo) {
int eachThreadChunkNum = fileInfo.totalChunks / concurrentThreadsNum; //每一个 线程承担的 上传片段
int remainedChunkNum = fileInfo.totalChunks % concurrentThreadsNum;
for (int threadIndex = 0; threadIndex < concurrentThreadsNum; threadIndex++) {
HashMap<String, Integer> perThreadInfo = new HashMap<>();
if (remainedChunkNum > threadIndex) {
perThreadInfo.put("eachThreadChunkNum", eachThreadChunkNum + 1);
perThreadInfo.put("endThreadChunkIndex", threadIndex * (eachThreadChunkNum + 1) + eachThreadChunkNum);
perThreadInfo.put("curThreadChunkIndex", threadIndex * (eachThreadChunkNum + 1));
perThreadInfo.put("startThreadChunkIndex", threadIndex * (eachThreadChunkNum + 1));
} else {
perThreadInfo.put("eachThreadChunkNum", eachThreadChunkNum);
perThreadInfo.put("endThreadChunkIndex", threadIndex * eachThreadChunkNum + eachThreadChunkNum - 1 + remainedChunkNum);
perThreadInfo.put("curThreadChunkIndex", threadIndex * eachThreadChunkNum + remainedChunkNum);
perThreadInfo.put("startThreadChunkIndex", threadIndex * eachThreadChunkNum + remainedChunkNum);
}
perThreadInfo.put("threadId", threadIndex);
fileInfo.threadInfo.add(perThreadInfo);
}
Logger.d(Tag, "fileInfo.threadInfo:" + fileInfo.threadInfo);
}
/**
* 获取当前片段大小
*
* @return
*/
private long getCurrentChunkSize(int curThreadChunkIndex, UpLoadFileInfo fileInfo) {
long tempLastChunkSize = fileInfo.fileSize % defaultChunkSize;
if (tempLastChunkSize != 0) {
if (curThreadChunkIndex == fileInfo.totalChunks - 1) {
//最后一片;
return tempLastChunkSize;
} else {
return defaultChunkSize;
}
} else {
return defaultChunkSize;
}
}
/**
* 重新计算当前上传大小;
*
* @return
*/
private void recomputeHasUploadSize(UpLoadFileInfo fileInfo) {
fileInfo.hasUploadSize = 0;
fileInfo.hasUploadSizeBeforeOneSec = 0;
for (int threadIndex = 0; threadIndex < concurrentThreadsNum; threadIndex++) {
HashMap<String, Integer> perThreadInfo = fileInfo.threadInfo.get(threadIndex);
int curThreadChunkIndex = perThreadInfo.get("curThreadChunkIndex");
int startThreadChunkIndex = perThreadInfo.get("startThreadChunkIndex");
for (int tempIndex = 0; tempIndex < curThreadChunkIndex - startThreadChunkIndex; tempIndex++) {
fileInfo.hasUploadSize += getCurrentChunkSize(tempIndex, fileInfo);
}
}
}
/**
* 开始或继续上传;
*
* @param fileInfo
*/
public void startUpload(final UpLoadFileInfo fileInfo) {
if (fileInfo.uploadStatus == UploadStatus.NOTSTART) {
upLoadPrepare(fileInfo);
} else {
recomputeHasUploadSize(fileInfo);
}
fileInfo.uploadStatus = ResumableUploadUtil.UploadStatus.UPLOADING;
fileInfo.fixedThreadPool = Executors.newFixedThreadPool(concurrentThreadsNum);
fileInfo.isBecauseDoBackgroundPause = false;
startCountTime(fileInfo);
if (fileInfo.resumableUploadListener != null) {
fileInfo.resumableUploadListener.onUpLoadStart(fileInfo);
}
//多线程上传;
for (int threadIndex = 0; threadIndex < concurrentThreadsNum; threadIndex++) {
HashMap<String, Integer> threadInfotemp = fileInfo.threadInfo.get(threadIndex);
Integer eachThreadChunkNum = threadInfotemp.get("eachThreadChunkNum");
Integer curThreadChunkIndex = threadInfotemp.get("curThreadChunkIndex");
Integer endThreadChunkIndex = threadInfotemp.get("endThreadChunkIndex");
Integer threadId = threadInfotemp.get("threadId");
Logger.e(Tag, "threadId:" + threadId + "," + "eachThreadChunkNum:" + eachThreadChunkNum + "," + "curThreadChunkIndex:" + curThreadChunkIndex + "endThreadChunkIndex:" + endThreadChunkIndex);
if (eachThreadChunkNum != 0) {
doUpload(threadIndex, fileInfo);
}
}
}
/**
* 上传暂停;
*
* @param becauseDoBackgroundPause 是否因为后台运行停止上传;
* @param fileInfo
*/
public void uploadPause(boolean becauseDoBackgroundPause, final UpLoadFileInfo fileInfo) {
fileInfo.uploadStatus = ResumableUploadUtil.UploadStatus.PAUSE;
fileInfo.isBecauseDoBackgroundPause = becauseDoBackgroundPause;
stopCountTime(fileInfo);
saveUploadFileInfo(fileInfo);
if (fileInfo.resumableUploadListener != null) {
handler.post(new Runnable() {
@Override
public void run() {
fileInfo.resumableUploadListener.onUpLoadPause(fileInfo);
}
});
}
if (fileInfo.fixedThreadPool != null) {
fileInfo.fixedThreadPool.shutdownNow();
fileInfo.fixedThreadPool = null;
}
}
/**
* 上传错误
*
* @param e 异常类别
* @param fileInfo
*/
public synchronized void uploadError(final Exception e, final UpLoadFileInfo fileInfo) {
//上传失败;1. 保存上传现场; 2. 提示失败原因
if (fileInfo.uploadStatus != UploadStatus.UPLOADING) {
return;
}
Logger.d(Tag, "uploadError");
fileInfo.uploadStatus = ResumableUploadUtil.UploadStatus.ERROR;
if (fileInfo.resumableUploadListener != null) {
handler.post(new Runnable() {
@Override
public void run() {
fileInfo.resumableUploadListener.onUpLoadError(e, fileInfo);
}
});
}
stopCountTime(fileInfo);
saveUploadFileInfo(fileInfo);
if (fileInfo.fixedThreadPool != null) {
fileInfo.fixedThreadPool.shutdownNow();
fileInfo.fixedThreadPool = null;
}
}
/**
* 上传成功
*
* @param fileInfo
* @param url
*/
private void uploadSuccess(final UpLoadFileInfo fileInfo, String url) {
Logger.d(Tag, "任务上传成功!!!");
fileInfo.uploadStatus = UploadStatus.SUCCESS;
removeUploadFileInfo(fileInfo);
stopCountTime(fileInfo);
fileInfo.fileUrl = url;
if (fileInfo.resumableUploadListener != null) {
handler.post(new Runnable() {
@Override
public void run() {
fileInfo.resumableUploadListener.onUpLoadSuccess(fileInfo);
}
});
}
if (fileInfo.fixedThreadPool != null) {
fileInfo.fixedThreadPool.shutdownNow();
fileInfo.fixedThreadPool = null;
}
}
/**
* 保存上传文件记录
*
* @param fileInfo
*/
public void saveUploadFileInfo(UpLoadFileInfo fileInfo) {
String cacheFileExtension = "";
if (fileInfo.fileType.equals(ConstantValue.FILETYPE_VIDEO)) {
cacheFileExtension = ConstantValue.cacheVideoExtension;
} else if (fileInfo.fileType.equals(ConstantValue.FILETYPE_DOC)) {
cacheFileExtension = ConstantValue.cacheDocExtension;
}
fileInfo.cacheUploadFilePath = CacheUploadInfo.saveUploadInfoFile + File.separator
+ fileInfo.fileType + fileInfo.uploadFileId + "." + cacheFileExtension;
Logger.d("cacheUploadFilePath", fileInfo.cacheUploadFilePath);
CacheUploadInfo.writeObjectToFile(fileInfo, fileInfo.cacheUploadFilePath);
}
/**
* 删除文件上传记录
*
* @param fileInfo
*/
public void removeUploadFileInfo(UpLoadFileInfo fileInfo) {
if (!StringUtil.isBlank(fileInfo.cacheUploadFilePath)) {
File cacheUploadfile = new File(fileInfo.cacheUploadFilePath);
if (cacheUploadfile.exists()) {
cacheUploadfile.delete();
}
}
}
/**
* 开启一个任务上传;
*
* @param threadIndex
* @param fileInfo
*/
private void doUpload(final int threadIndex, final UpLoadFileInfo fileInfo) {
if (fileInfo.fixedThreadPool == null)
return;
if (!fileInfo.fixedThreadPool.isShutdown()) {
fileInfo.fixedThreadPool.execute(
new Runnable() {
@Override
public void run() {
try {
byte[] headerInfo = buildHeaderInfo(threadIndex, fileInfo);
byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
HttpURLConnection conn = initHttpConnection(fileInfo.remoteUrl);
OutputStream out = conn.getOutputStream();
out.write(headerInfo);
writeToServer(threadIndex, conn, out, endInfo, fileInfo);//写数据;
} catch (Exception e) {
e.printStackTrace();
uploadError(e, fileInfo);
}
}
});
}
}
/**
* 构建上传参数;
*
* @param threadIndex
* @param fileInfo
* @return
* @throws UnsupportedEncodingException
*/
private byte[] buildHeaderInfo(int threadIndex, UpLoadFileInfo fileInfo) throws UnsupportedEncodingException {
HashMap<String, String> params = new HashMap<>();
params.put("cloudUserGUID", fileInfo.comParams.get("cloudUserGUID"));
params.put("notifyUrl", fileInfo.uploadSuccessCallback);
params.put("fileType", fileInfo.fileType);
params.put("storageServerGUID", fileInfo.storageServerGUID);
params.put("resumableType", "application/x-zip-compressed");
params.put("resumableTotalSize", fileInfo.fileSize + "");
params.put("resumableIdentifier", fileInfo.fileSize + "-" + fileInfo.fileName + "");
params.put("resumableFilename", fileInfo.fileName + "");
params.put("resumableRelativePath", fileInfo.filePath);
params.put("resumableChunkSize", defaultChunkSize + ""); //分片大小;
params.put("resumableTotalChunks", fileInfo.totalChunks + "");
HashMap<String, Integer> perThreadInfo = fileInfo.threadInfo.get(threadIndex);
int curThreadChunkIndex = perThreadInfo.get("curThreadChunkIndex");
params.put("resumableCurrentChunkSize", getCurrentChunkSize(curThreadChunkIndex, fileInfo) + ""); //当前片大小
params.put("resumableChunkNumber", curThreadChunkIndex + 1 + "");
StringBuilder sb = new StringBuilder();
for (String key : params.keySet()) {
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"" + key + "\""
+ "\r\n");
sb.append("\r\n");
sb.append(params.get(key) + "\r\n");
}
//上传文件的头
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileInfo.fileName + "\"" + "\r\n");
sb.append("Content-Type: application/octet-stream" + "\r\n");
sb.append("\r\n");
// Logger.d(Tag, "headerInfo:" + sb.toString());
Logger.d("buildHeaderInfo", "threadIndex:" + threadIndex + ",resumableTotalSize:"
+ fileInfo.fileSize + ",resumableTotalChunks:"
+ fileInfo.totalChunks + ",resumableCurrentChunkSize:"
+ getCurrentChunkSize(curThreadChunkIndex, fileInfo) + ",resumableChunkNumber" + (curThreadChunkIndex + 1) + "");
byte[] haderInfoBytes = sb.toString().getBytes("UTF-8");
params = null;
sb = null;
return haderInfoBytes;
}
/**
* 初始化 http连接
*
* @param url
* @return
* @throws IOException
*/
private HttpURLConnection initHttpConnection(URL url) throws IOException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + BOUNDARY);
conn.setConnectTimeout(30 * 1000);//设置0.5min 超时
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Charset", "UTF-8");
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setDoOutput(true);
return conn;
}
//写每一个片段数据
public void writeToServer(int threadIndex, HttpURLConnection conn, OutputStream out, byte[] endInfo, final UpLoadFileInfo fileInfo) throws Exception {
RandomAccessFile raf = new RandomAccessFile(new File(fileInfo.filePath), "r");//负责读取数据
float filesize = fileInfo.fileSize;
HashMap<String, Integer> perThreadInfo = fileInfo.threadInfo.get(threadIndex);
int curThreadChunkIndex = perThreadInfo.get("curThreadChunkIndex");
raf.seek(defaultChunkSize * curThreadChunkIndex);
byte b[] = new byte[defaultBufferSize];//暂存容器
int n = 0; //本次写出字节数
long readLength = 0;//记录此片已读字节数
while (readLength < getCurrentChunkSize(curThreadChunkIndex, fileInfo)) {
//判断是否在后台运行
boolean runningOnBackground = ActivityStack.isRunningOnBackground(MyApplicationLike.getContext());
if (runningOnBackground) {
uploadPause(true, fileInfo);
}
if (fileInfo.uploadStatus != UploadStatus.UPLOADING) {
return;
}
n = raf.read(b, 0, defaultBufferSize);
out.write(b, 0, n);
readLength += n;
fileInfo.hasUploadSize += n;
fileInfo.updateTextProgress();
fileInfo.uploadProgress = (fileInfo.hasUploadSize / filesize) * 100;
Logger.d(Tag, "进度:" + fileInfo.uploadProgress + "%" + ",hasUploadSize:" + fileInfo.hasUploadSize + ",filesize:" + filesize);
if (fileInfo.resumableUploadListener != null) {
handler.post(new Runnable() {
@Override
public void run() {
if (fileInfo.uploadStatus == UploadStatus.UPLOADING) {
fileInfo.resumableUploadListener.onUpLoading(fileInfo);
}
}
});
}
}
out.write(endInfo);
out.close();
raf.close();
//工作线程;
raf = null;
b = null;
handleWriterResult(threadIndex, conn, fileInfo);
}
/**
* 处理每一片上传结果
*
* @param threadIndex
* @param conn
* @param fileInfo
* @throws Exception
*/
private void handleWriterResult(int threadIndex, HttpURLConnection conn, final UpLoadFileInfo fileInfo) throws Exception {
//response:
final String responseMsg = getResponseMsg(conn);
Logger.d(Tag, "responseMsg:" + responseMsg);
//response:==================
HashMap<String, Integer> perThreadInfo = fileInfo.threadInfo.get(threadIndex);
int curThreadChunkIndex = perThreadInfo.get("curThreadChunkIndex");
int eachThreadChunkNum = perThreadInfo.get("eachThreadChunkNum");
int startThreadChunkIndex = perThreadInfo.get("startThreadChunkIndex");
int endThreadChunkIndex = perThreadInfo.get("endThreadChunkIndex");
if (curThreadChunkIndex != endThreadChunkIndex) {
//非最后一段
if (conn.getResponseCode() == 200) {
//继续上传
Logger.d("handle2WriterResult", "handleWriterResult: " + ",threadIndex:" + threadIndex +
" ,CurrentChunkSize:" + getCurrentChunkSize(curThreadChunkIndex, fileInfo) +
",curThreadChunkIndex:" + curThreadChunkIndex +
",eachThreadChunkNum:" + eachThreadChunkNum +
",startThreadChunkIndex:" + startThreadChunkIndex +
",endThreadChunkIndex:" + endThreadChunkIndex +
",totalChunks:" + fileInfo.totalChunks);
Logger.d(Tag, "工作线程:" + threadIndex + "上传成功" + ",curThreadChunkIndex:" + curThreadChunkIndex);
if (curThreadChunkIndex < endThreadChunkIndex) {
curThreadChunkIndex += 1;//每个工作线程的当前 片
perThreadInfo.put("curThreadChunkIndex", curThreadChunkIndex);
}
doUpload(threadIndex, fileInfo);
Logger.d(Tag, "继续上传!!!");
} else {
uploadError(null, fileInfo);
}
} else {
//最后一段
if (conn.getResponseCode() == 200) {
//上传成功
Logger.d("handle2WriterResult", "handleWriterResult: " + ",threadIndex:" + threadIndex +
" ,CurrentChunkSize:" + getCurrentChunkSize(curThreadChunkIndex, fileInfo) +
",curThreadChunkIndex:" + curThreadChunkIndex +
",eachThreadChunkNum:" + eachThreadChunkNum +
",startThreadChunkIndex:" + startThreadChunkIndex +
",endThreadChunkIndex:" + endThreadChunkIndex +
",totalChunks:" + fileInfo.totalChunks);
Logger.d(Tag, "工作线程:" + threadIndex + "上传成功最后一段!!!");
if (curThreadChunkIndex < endThreadChunkIndex) {
curThreadChunkIndex += 1;//每个工作线程的当前 片
perThreadInfo.put("curThreadChunkIndex", curThreadChunkIndex);
}
if (!StringUtil.isBlank(responseMsg)) {
JSONObject object = new JSONObject(responseMsg);
final String url = String.valueOf(object.get("data"));
if (!StringUtil.isBlank(url)) {
uploadSuccess(fileInfo, url);
}
object = null;
}
} else {
uploadError(null, fileInfo);
}
}
}
/**
* 获取到每一片 上传后结果
*
* @param conn
* @return
* @throws IOException
*/
private String getResponseMsg(HttpURLConnection conn) throws IOException {
StringBuilder sbResponse = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(conn
.getInputStream(), "UTF-8"));
String inputLine;
while ((inputLine = in.readLine()) != null) {
sbResponse.append(inputLine);
}
in.close();
String responseMsg = sbResponse.toString();
in = null;
sbResponse = null;
return responseMsg;
}
public void startCountTime(UpLoadFileInfo fileInfo) {
if (fileInfo.timerTask == null) {
fileInfo.timerTask = new UploadTimerTask(fileInfo);
}
fileInfo.timerTask.start();
}
public void stopCountTime(UpLoadFileInfo fileInfo) {
if (fileInfo.timerTask != null) {
fileInfo.timerTask.stop();
}
}
public enum UploadStatus {
UPLOADING, SUCCESS, PAUSE, NOTSTART, ERROR
}
public interface ResumableUploadListener {
void onUpLoading(UpLoadFileInfo fileInfo);
void onUpLoadSuccess(UpLoadFileInfo fileInfo);
void onUpLoadError(Exception e, UpLoadFileInfo fileInfo);
void onUpLoadStart(UpLoadFileInfo fileInfo);
void onUpLoadPause(UpLoadFileInfo fileInfo);
}
public void setResumableUploadListener(ResumableUploadUtil.ResumableUploadListener listener, UpLoadFileInfo fileInfo) {
fileInfo.resumableUploadListener = listener;
}
}
UpLoadFileInfo 用于保存上传记录:
package upload;
import upload.ResumableUploadUtil;
import upload.UploadTimerTask;
import upload.StringUtil;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import static upload.StringUtil.getDataSize;
public class UpLoadFileInfo implements Serializable {
public URL remoteUrl;//上传地址
public String fileUrl;//上传成功后文件地址
public String fileName;
public String filePath;
public String cacheUploadFilePath;
public long fileSize;
public long hasUploadSize;
public long hasUploadSizeBeforeOneSec;
public long ModifiedDate;
public long dbId; // id in the database, if is from database
public float uploadProgress;
public String uploadFileId;
public int recLen = 0;//已经上传时间
public HashMap<String, String> comParams;
public String rootDir;
public String storageServerGUID;
public String uploadSuccessCallback;
public String fileType;
public String extension;
public String textProgress = updateTextProgress();
public String uploadSpeed = updateUploadSpeed();
public boolean isBecauseDoBackgroundPause = false;
public int totalChunks = 0;
public transient UploadTimerTask timerTask;
public transient ExecutorService fixedThreadPool;
/**
* 上传监听
*/
public transient ResumableUploadUtil.ResumableUploadListener resumableUploadListener;
/**
* 上传状态
*/
public ResumableUploadUtil.UploadStatus uploadStatus = ResumableUploadUtil.UploadStatus.NOTSTART;
/**
* 工作线程信息
*/
public List<HashMap<String, Integer>> threadInfo = new ArrayList<>();
public String updateTextProgress() {
return textProgress = StringUtil.getDataSize(hasUploadSize) + "/" + StringUtil.getDataSize(fileSize);
}
public String updateUploadSpeed() {
return uploadSpeed = getDataSize(hasUploadSize - hasUploadSizeBeforeOneSec) + "/s";
}
@Override
public String toString() {
return "UpLoadFileInfo{" +
"UploadTimerTask=" + timerTask +
", remoteUrl=" + remoteUrl +
", fileUrl='" + fileUrl + '\'' +
", fileName='" + fileName + '\'' +
", filePath='" + filePath + '\'' +
", cacheUploadFilePath='" + cacheUploadFilePath + '\'' +
", fileSize=" + fileSize +
", hasUploadSize=" + hasUploadSize +
", ModifiedDate=" + ModifiedDate +
", dbId=" + dbId +
", uploadProgress=" + uploadProgress +
", uploadFileId=" + uploadFileId +
", recLen=" + recLen +
", comParams=" + comParams +
", rootDir='" + rootDir + '\'' +
", storageServerGUID='" + storageServerGUID + '\'' +
", uploadSuccessCallback='" + uploadSuccessCallback + '\'' +
", fileType='" + fileType + '\'' +
", extension='" + extension + '\'' +
", textProgress='" + textProgress + '\'' +
", uploadSpeed='" + uploadSpeed + '\'' +
", fixedThreadPool=" + fixedThreadPool +
", resumableUploadListener=" + resumableUploadListener +
", uploadStatus=" + uploadStatus +
", totalChunks=" + totalChunks +
", isBecauseDoBackgroundPause=" + isBecauseDoBackgroundPause +
", threadInfo=" + threadInfo +
'}';
}
}
UploadTimerTask 用于计时,计算上传速度:
package upload;
import android.os.SystemClock;
import upload.UpLoadFileInfo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class UploadTimerTask {
private UpLoadFileInfo upLoadFileInfo;
private boolean stop = true;
private ExecutorService executorService = Executors.newSingleThreadExecutor();
public UploadTimerTask(UpLoadFileInfo upLoadFileInfo) {
this.upLoadFileInfo = upLoadFileInfo;
}
public void start() {
this.stop = false;
executorService.execute(
new Runnable() {
@Override
public void run() {
while (!stop) {
upLoadFileInfo.hasUploadSizeBeforeOneSec = upLoadFileInfo.hasUploadSize;
SystemClock.sleep(1000);
upLoadFileInfo.recLen++;
upLoadFileInfo.updateUploadSpeed();
}
}
}
);
}
public void stop() {
this.stop = true;
}
}