分片上传

SDK 提供了分片上传(Multipart Upload)功能,可以将要上传的文件分成多个数据块(US3 里又称之为 Part)来分别上传。完整代码详见 。

分片上传流程

SDK 提供以下方法完成分片上传,接口说明如下

接口名 说明
InitiateMultipartUpload 初始化分片上传,US3 API 文档详见 InitiateMultipartUpload
UploadPart 上传一个分片,US3 API 文档详见 UploadPart
AbortMultipartUpload 终止分片上传,US3 API 文档详见 AbortMultipartUpload
FinishMultipartUpload 完成分片上传,US3 API 文档详见 FinishMultipartUpload

一个完整的分片上传分为初始化分片 -> 上传分片 -> 完成分片三个阶段,具体见分片上传流程

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
public class MultiUploadSample {
    private static final String TAG = "MultiUploadSample";
    private static ObjectConfig config = new ObjectConfig("cn-sh2", "ufileos.com");

    public static void main(String[] args) {
        File file = new File("");
        String keyName = file.getName();
        String bucketName = "";

        // 先初始化分片上环请求
        MultiUploadInfo state = initMultiUpload(file, keyName, bucketName);
        if (state == null)
            return;

        JLog.D(TAG, String.format("[init state] = %s", (state == null ? "null" : state.toString())));

        List<MultiUploadPartState> partStates = multiUpload(file, state);
        // 若上传分片结果列表为空,则失败,需中断上传操作。否则完成上传
        if (partStates == null || partStates.isEmpty())
            abortMultiUpload(state);
        else
            finishMultiUpload(state, partStates);  //完成分片上传
    }

    public static MultiUploadInfo initMultiUpload(File file, String keyName, String bucketName) {
        try {
            // MimeTypeUtil可能支持的type类型不全,用户可以按需自行填写
            String mimeType = MimeTypeUtil.getMimeType(file);
            return UfileClient.object(Constants.OBJECT_AUTHORIZER, config)
                    .initMultiUpload(keyName, mimeType, bucketName)
                    /**
                     * 配置文件存储类型,分别是标准、低频、冷存,对应有效值:STANDARD | IA | ARCHIVE
                     */
                    .withStorageType(StorageType.STANDARD)
                    /**
                     * 为云端对象配置自定义数据,每次调用将会替换之前数据。
                     * 所有的自定义数据总大小不能超过 8KB。
                     */
//                    .withMetaDatas()
                    /**
                     * 为云端对象添加自定义数据,可直接调用,无须先调用withMetaDatas
                     * key不能为空或者""
                     *
                     */
//                    .addMetaData(new Parameter<>("key","value"))
                    .execute();
        } catch (UfileClientException e) {
            e.printStackTrace();
        } catch (UfileServerException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static List<MultiUploadPartState> multiUpload(File file, MultiUploadInfo state) {
        List<MultiUploadPartState> partStates = null;
        byte[] buffer = new byte[state.getBlkSize()];
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            int len = 0;
            int count = 0;

            partStates = new ArrayList<>();
            // 将数据根据state中指定的大小进行分片
            while ((len = is.read(buffer)) > 0) {
                final int index = count++;
                byte[] sendData = Arrays.copyOf(buffer, len);
                int uploadCount = 0;

                // 可支持重试3次上传
                while (uploadCount < 3) {
                    try {
                        MultiUploadPartState partState = UfileClient.object(Constants.OBJECT_AUTHORIZER, config)
                                .multiUploadPart(state, sendData, index)
                                /**
                                 * 指定progress callback的间隔
                                 */
//                                .withProgressConfig(ProgressConfig.callbackWithPercent(50))
                                /**
                                 * 配置进度监听
                                 */
                                .setOnProgressListener(new OnProgressListener() {
                                    @Override
                                    public void onProgress(long bytesWritten, long contentLength) {
                                        JLog.D(TAG, String.format("[index] = %d\t[progress] = %d%% - [%d/%d]", index,
                                                (int) (bytesWritten * 1.f / contentLength * 100), bytesWritten, contentLength));
                                    }
                                })
                                .execute();
                        if (partState == null) {
                            uploadCount++;
                            continue;
                        }

                        partStates.add(partState);
                        break;
                    } catch (UfileClientException e) {
                        e.printStackTrace();
                        // 尝试次数+1
                        uploadCount++;
                    } catch (UfileServerException e) {
                        e.printStackTrace();
                        // 尝试次数+1
                        uploadCount++;
                    }
                }

                if (uploadCount == 3)
                    return null;
            }

            return partStates;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileUtil.close(is);
        }

        return null;
    }

    public static void abortMultiUpload(MultiUploadInfo info) {
        try {
            BaseObjectResponseBean abortRes = UfileClient.object(Constants.OBJECT_AUTHORIZER, config)
                    .abortMultiUpload(info)
                    .execute();
            JLog.D(TAG, "abort->" + abortRes.toString());
        } catch (UfileClientException e) {
            e.printStackTrace();
        } catch (UfileServerException e) {
            e.printStackTrace();
        }
    }

    public static MultiUploadResponse finishMultiUpload(MultiUploadInfo state, List<MultiUploadPartState> partStates) {
        try {
            /**
             * 上传回调策略
             * 必须填写回调接口url(目前仅支持http,不支持https),可选填回调参数,回调参数请自行决定是否需要urlencode。
             * 若配置上传回调,则上传接口的回调将会透传回调接口的response,包括httpCode
             */
            PutPolicy putPolicy = new PutPolicyForCallback.Builder("http://xxx.xxx.xxx.xxx[:port][/path]")
                    .addCallbackBody(new PolicyParam("key", "value"))
                    .build();
            MultiUploadResponse res = UfileClient.object(Constants.OBJECT_AUTHORIZER, config)
                    .finishMultiUpload(state, partStates)
                    /**
                     * 配置上传回调策略
                     */
//                .withPutPolicy(putPolicy)
                    /**
                     * 为云端对象配置自定义数据,每次调用将会替换之前数据。
                     * 所有的自定义数据总大小不能超过 8KB。
                     */
//                    .withMetaDatas()
                    /**
                     * 为云端对象添加自定义数据,可直接调用,无须先调用withMetaDatas
                     * key不能为空或者""
                     *
                     */
//                    .addMetaData(new Parameter<>("key","value"))
                    /**
                     * 配置用户自定义元数据设置方式
                     * 具体参数配置可见 {@link MetadataDirective}
                     */
//                    .withMetadataDirective(MetadataDirective.UNCHANGED)
                    .execute();
            JLog.D(TAG, "finish->" + res.toString());
            return res;
        } catch (UfileClientException e) {
            e.printStackTrace();
        } catch (UfileServerException e) {
            e.printStackTrace();
        }
        return null;
    }

}

错误码

HTTP 状态码 RetCode ErrMsg 描述
400 -148653 bucket not exists 存储空间不存在
400 -15036 check md5 failed MD5校验失败
401 -148643 no authorization found 上传凭证错误
403 -148643 invalid signature API公私钥错误KeyName包含%、#、?