MultiUploader.java
11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
package com.cnlive.cnUploadDemo;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.cnlive.libs.upload.listener.CNAbortMultipartUploadResponseListener;
import com.cnlive.libs.upload.listener.CNCompleteMultipartUploadResponseListener;
import com.cnlive.libs.upload.listener.CNInitiateMultipartUploadResponseListener;
import com.cnlive.libs.upload.listener.CNListPartsResponseListener;
import com.cnlive.libs.upload.listener.CNUploadPartResponseListener;
import com.cnlive.libs.upload.model.CNPart;
import com.cnlive.libs.upload.model.CNPartETag;
import com.cnlive.libs.upload.model.UploadError;
import com.cnlive.libs.upload.model.acl.CNCannedAccessControlList;
import com.cnlive.libs.upload.model.result.CNCompleteMultipartUploadResult;
import com.cnlive.libs.upload.model.result.CNInitiateMultipartUploadResult;
import com.cnlive.libs.upload.model.result.CNListPartsResult;
import com.cnlive.libs.upload.services.CNAbortMultipartUploadRequest;
import com.cnlive.libs.upload.services.CNCompleteMultipartUploadRequest;
import com.cnlive.libs.upload.services.CNInitiateMultipartUploadRequest;
import com.cnlive.libs.upload.services.CNUploadPartRequest;
import com.cnlive.libs.upload.upload.CNUpload;
import org.apache.http.Header;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Lynn on 2017/8/7.
* 分块上传的更高层的封装,可以修改partSize等变量,log输出格式等。也可以在initiateMultipartUpload这一步之后加入保存uploadid的逻辑(最好存在你自己后台)。
* 当然也可参考MultiUploader.java自行实现你的接口。
*/
public class MultiUploader {
final private String TAG = "MultiUploader";
private String key;
private File file;
private long partSize = 5 * 1024 * 1024; // 5MB
private String uploadId;
private CNUpload cnUpload;
final static private int INIT_DONE = 0;
final static private int PARTS_DONE = 1;
final static private int COMPLETE_DONE = 2;
final static private int GET_UPLOADED_DONE = 3;
final List<CNPartETag> doneParts = Collections.synchronizedList(new ArrayList());
private void create(CNUpload cnUpload, String key, File file, String uploadId, long partSize) {
this.cnUpload = cnUpload;
this.key = key;
this.file = file;
this.uploadId = uploadId;
this.partSize = partSize;
}
public MultiUploader(CNUpload cnUpload, String key, File file, String uploadId, long partSize) {
create(cnUpload, key, file, uploadId, partSize);
}
public MultiUploader(CNUpload cnUpload, String key, File file, String uploadId) {
create(cnUpload, key, file, uploadId, partSize);
}
public MultiUploader(CNUpload cnUpload, String key, File file) {
create(cnUpload, key, file, null, partSize);
}
public MultiUploader(CNUpload cnUpload, String key, File file, long partSize) {
create(cnUpload, key, file, null, partSize);
}
public String getKey() {
return key;
}
public String getUploadId() {
return uploadId;
}
abstract class MyUploadPartResponceHandler implements CNUploadPartResponseListener {
private String key;
private int partNo;
private String uploadId;
public abstract void onSuccess(int statesCode, Header[] responceHeaders, CNPartETag result, String key, int partNo, String uploadId);
public abstract void onFailure(int statesCode, UploadError error, Header[] responceHeaders, String response, Throwable throwable
, String key, int partNo, String uploadId);
public abstract void onTaskProgress(double progress, String key, int partNo, String uploadId);
public MyUploadPartResponceHandler(String key, int partNo, String uploadId) {
this.key = key;
this.partNo = partNo;
this.uploadId = uploadId;
}
@Override
public void onTaskProgress(double progress) {
onTaskProgress(progress, key, partNo, uploadId);
}
@Override
public void onSuccess(int statesCode, Header[] responceHeaders, CNPartETag result) {
onSuccess(statesCode, responceHeaders, result, key, partNo, uploadId);
}
@Override
public void onFailure(int statesCode, UploadError error, Header[] responceHeaders, String response, Throwable throwable) {
onFailure(statesCode, error, responceHeaders, response, throwable, key, partNo, uploadId);
}
}
private final MyHandler mHandler = new MyHandler();
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case INIT_DONE:
uploadParts();
break;
case PARTS_DONE:
completeUpload();
break;
case GET_UPLOADED_DONE:
List<CNPartETag> res = (List<CNPartETag>) msg.obj;
reUpload(res);
break;
default:
break;
}
}
}
/*
return false, if uploadId hasbeen got
*/
public boolean upload() {
if (uploadId != null) return false;
CNInitiateMultipartUploadRequest request = new CNInitiateMultipartUploadRequest(key);
cnUpload.initiateMultipartUpload(request, new CNInitiateMultipartUploadResponseListener() {
@Override
public void onFailure(int statesCode, UploadError error, Header[] responceHeaders, String response, Throwable paramThrowable) {
Log.w(TAG, "init multiupload fail, statesCode=" + statesCode, paramThrowable);
}
@Override
public void onSuccess(int statesCode, Header[] responceHeaders, CNInitiateMultipartUploadResult result) {
uploadId = result.getUploadId();
Log.d(TAG, "init multiupload success, uploadId=" + uploadId + ",key=" + key);
mHandler.sendEmptyMessage(INIT_DONE);
}
});
return true;
}
private List<CNPartETag> convertPart(List<CNPart> list) {
List<CNPartETag> res = new ArrayList<CNPartETag>();
for (CNPart p : list) {
res.add(new CNPartETag(p.getPartNumber(), p.getETag()));
}
return res;
}
/*
Get from ks3 server. call list parts API.
*/
public void getUploadedParts() {
final List<CNPartETag> res = new ArrayList<CNPartETag>();
final CNListPartsResponseListener listPartsResponseHandler = new CNListPartsResponseListener() {
@Override
public void onFailure(int statesCode, UploadError error, Header[] responceHeaders, String response, Throwable paramThrowable) {
Log.w(TAG, "list parts, statesCode=" + statesCode, paramThrowable);
}
@Override
public void onSuccess(int statesCode, Header[] responceHeaders, CNListPartsResult listPartsResult) {
res.addAll(convertPart(listPartsResult.getCNParts()));
if (!listPartsResult.isTruncated())
mHandler.sendMessage(Message.obtain(mHandler, GET_UPLOADED_DONE, res));
else
Log.e(TAG, "File size too largs. You may not use phone to upload");
}
};
cnUpload.listParts(key, uploadId, listPartsResponseHandler);
}
public List<Integer> getLeftParts(List<CNPartETag> uploadedParts) {
List<Integer> res = new ArrayList<Integer>();
long start = 0L;
int partNumber = 1;
Set<Integer> set = new HashSet<Integer>();
for (CNPartETag p : uploadedParts) {
set.add(p.getPartNumber());
}
while (start < file.length()) {
if (!set.contains(partNumber))
res.add(partNumber);
partNumber++;
start += partSize;
}
return res;
}
public void reUpload() {
if (uploadId == null) {
Log.i(TAG, "no upload id, cannot reupload");
return;
}
getUploadedParts();
}
public void reUpload(final List<CNPartETag> uploadedParts) {
final List<Integer> leftParts = getLeftParts(uploadedParts);
final int N = leftParts.size() + uploadedParts.size();
doneParts.addAll(uploadedParts);
if (leftParts.isEmpty())
mHandler.sendEmptyMessage(PARTS_DONE);
for (Integer partNumber : leftParts) {
long offset = partNumber * partSize - partSize;
CNUploadPartRequest uploadPartRequest = new CNUploadPartRequest(key, uploadId, file,
offset, partNumber, Math.min(file.length() - offset, partSize));
cnUpload.uploadPart(uploadPartRequest, new MyUploadPartResponceHandler(key, partNumber, uploadId) {
@Override
public void onSuccess(int statesCode, Header[] responceHeaders, CNPartETag result, String key, int partNo, String uploadId) {
result.setPartNumber(partNo);
doneParts.add(result);
if (doneParts.size() == N)
mHandler.sendEmptyMessage(PARTS_DONE);
}
@Override
public void onFailure(int statesCode, UploadError error, Header[] responceHeaders, String response, Throwable throwable,
String key, int partNo, String uploadId) {
// you may record this failure info in file, database, or backend
Log.w(TAG, "upload part fail, uploadId=" + uploadId + ",key=" + key + ",partNo=" + partNo, throwable);
}
@Override
public void onTaskProgress(double progress, String key, int partNo, String uploadId) {
if (progress >= 99)
Log.i(TAG, "progress:" + progress + ",key=" + key + ",partNo" + partNo);
}
});
}
}
private void uploadParts() {
reUpload(new ArrayList<CNPartETag>());
}
private void completeUpload() {
cnUpload.completeMultipartUpload(key, uploadId, doneParts, new CNCompleteMultipartUploadResponseListener() {
@Override
public void onFailure(int statesCode, UploadError error, Header[] responceHeaders, String response, Throwable paramThrowable) {
Log.w(TAG, "complete upload fail, statusCode=" + statesCode, paramThrowable);
}
@Override
public void onSuccess(int statesCode, Header[] responceHeaders, CNCompleteMultipartUploadResult result) {
Log.i(TAG, "complete upload, key=" + key);
}
});
}
/**
* 此暂停方法,会立即暂停,继续上传时,当前块重新上传
* 也可以通过不上传下一块的方法暂停上传
*
* @param context
*/
public void pause(Context context) {
cnUpload.pause(context);
}
}