MultiUploader.java 11.1 KB
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);
    }
}