File: multipart.go

package info (click to toggle)
golang-github-aliyun-aliyun-oss-go-sdk 1.5.0%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 584 kB
  • sloc: makefile: 6
file content (281 lines) | stat: -rw-r--r-- 9,774 bytes parent folder | download | duplicates (2)
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
package oss

import (
	"bytes"
	"encoding/xml"
	"io"
	"net/http"
	"os"
	"sort"
	"strconv"
)

//
// InitiateMultipartUpload 初始化分片上传任务。
//
// objectKey  Object名称。
// options    上传时可以指定Object的属性,可选属性有CacheControl、ContentDisposition、ContentEncoding、Expires、
// ServerSideEncryption、Meta,具体含义请参考
// https://help.aliyun.com/document_detail/oss/api-reference/multipart-upload/InitiateMultipartUpload.html
//
// InitiateMultipartUploadResult 初始化后操作成功的返回值,用于后面的UploadPartFromFile、UploadPartCopy等操作。error为nil时有效。
// error  操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) InitiateMultipartUpload(objectKey string, options ...Option) (InitiateMultipartUploadResult, error) {
	var imur InitiateMultipartUploadResult
	opts := addContentType(options, objectKey)
	resp, err := bucket.do("POST", objectKey, "uploads", "uploads", opts, nil, nil)
	if err != nil {
		return imur, err
	}
	defer resp.Body.Close()

	err = xmlUnmarshal(resp.Body, &imur)
	return imur, err
}

//
// UploadPart 上传分片。
//
// 初始化一个Multipart Upload之后,可以根据指定的Object名和Upload ID来分片(Part)上传数据。
// 每一个上传的Part都有一个标识它的号码(part number,范围是1~10000)。对于同一个Upload ID,
// 该号码不但唯一标识这一片数据,也标识了这片数据在整个文件内的相对位置。如果您用同一个part号码,上传了新的数据,
// 那么OSS上已有的这个号码的Part数据将被覆盖。除了最后一片Part以外,其他的part最小为100KB;
// 最后一片Part没有大小限制。
//
// imur        InitiateMultipartUpload成功后的返回值。
// reader      io.Reader 需要分片上传的reader。
// size        本次上传片Part的大小。
// partNumber  本次上传片(Part)的编号,范围是1~10000。如果超出范围,OSS将返回InvalidArgument错误。
//
// UploadPart 上传成功的返回值,两个成员PartNumber、ETag。PartNumber片编号,即传入参数partNumber;
// ETag及上传数据的MD5。error为nil时有效。
// error 操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) UploadPart(imur InitiateMultipartUploadResult, reader io.Reader,
	partSize int64, partNumber int, options ...Option) (UploadPart, error) {
	request := &UploadPartRequest{
		InitResult: &imur,
		Reader:     reader,
		PartSize:   partSize,
		PartNumber: partNumber,
	}

	result, err := bucket.DoUploadPart(request, options)

	return result.Part, err
}

//
// UploadPartFromFile 上传分片。
//
// imur           InitiateMultipartUpload成功后的返回值。
// filePath       需要分片上传的本地文件。
// startPosition  本次上传文件片的起始位置。
// partSize       本次上传文件片的大小。
// partNumber     本次上传文件片的编号,范围是1~10000。
//
// UploadPart 上传成功的返回值,两个成员PartNumber、ETag。PartNumber片编号,传入参数partNumber;
// ETag上传数据的MD5。error为nil时有效。
// error 操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) UploadPartFromFile(imur InitiateMultipartUploadResult, filePath string,
	startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
	var part = UploadPart{}
	fd, err := os.Open(filePath)
	if err != nil {
		return part, err
	}
	defer fd.Close()
	fd.Seek(startPosition, os.SEEK_SET)

	request := &UploadPartRequest{
		InitResult: &imur,
		Reader:     fd,
		PartSize:   partSize,
		PartNumber: partNumber,
	}

	result, err := bucket.DoUploadPart(request, options)

	return result.Part, err
}

//
// DoUploadPart 上传分片。
//
// request 上传分片请求。
//
// UploadPartResult 上传分片请求返回值。
// error  操作无错误为nil,非nil为错误信息。
//
func (bucket Bucket) DoUploadPart(request *UploadPartRequest, options []Option) (*UploadPartResult, error) {
	listener := getProgressListener(options)
	params := "partNumber=" + strconv.Itoa(request.PartNumber) + "&uploadId=" + request.InitResult.UploadID
	opts := []Option{ContentLength(request.PartSize)}
	resp, err := bucket.do("PUT", request.InitResult.Key, params, params, opts,
		&io.LimitedReader{R: request.Reader, N: request.PartSize}, listener)
	if err != nil {
		return &UploadPartResult{}, err
	}
	defer resp.Body.Close()

	part := UploadPart{
		ETag:       resp.Headers.Get(HTTPHeaderEtag),
		PartNumber: request.PartNumber,
	}

	if bucket.getConfig().IsEnableCRC {
		err = checkCRC(resp, "DoUploadPart")
		if err != nil {
			return &UploadPartResult{part}, err
		}
	}

	return &UploadPartResult{part}, nil
}

//
// UploadPartCopy 拷贝分片。
//
// imur           InitiateMultipartUpload成功后的返回值。
// copySrc        源Object名称。
// startPosition  本次拷贝片(Part)在源Object的起始位置。
// partSize       本次拷贝片的大小。
// partNumber     本次拷贝片的编号,范围是1~10000。如果超出范围,OSS将返回InvalidArgument错误。
// options        copy时源Object的限制条件,满足限制条件时copy,不满足时返回错误。可选条件有CopySourceIfMatch、
// CopySourceIfNoneMatch、CopySourceIfModifiedSince  CopySourceIfUnmodifiedSince,具体含义请参看
// https://help.aliyun.com/document_detail/oss/api-reference/multipart-upload/UploadPartCopy.html
//
// UploadPart 上传成功的返回值,两个成员PartNumber、ETag。PartNumber片(Part)编号,即传入参数partNumber;
// ETag及上传数据的MD5。error为nil时有效。
// error 操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) UploadPartCopy(imur InitiateMultipartUploadResult, srcBucketName, srcObjectKey string,
	startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
	var out UploadPartCopyResult
	var part UploadPart

	opts := []Option{CopySource(srcBucketName, srcObjectKey),
		CopySourceRange(startPosition, partSize)}
	opts = append(opts, options...)
	params := "partNumber=" + strconv.Itoa(partNumber) + "&uploadId=" + imur.UploadID
	resp, err := bucket.do("PUT", imur.Key, params, params, opts, nil, nil)
	if err != nil {
		return part, err
	}
	defer resp.Body.Close()

	err = xmlUnmarshal(resp.Body, &out)
	if err != nil {
		return part, err
	}
	part.ETag = out.ETag
	part.PartNumber = partNumber

	return part, nil
}

//
// CompleteMultipartUpload 提交分片上传任务。
//
// imur   InitiateMultipartUpload的返回值。
// parts  UploadPart/UploadPartFromFile/UploadPartCopy返回值组成的数组。
//
// CompleteMultipartUploadResponse  操作成功后的返回值。error为nil时有效。
// error  操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) CompleteMultipartUpload(imur InitiateMultipartUploadResult,
	parts []UploadPart) (CompleteMultipartUploadResult, error) {
	var out CompleteMultipartUploadResult

	sort.Sort(uploadParts(parts))
	cxml := completeMultipartUploadXML{}
	cxml.Part = parts
	bs, err := xml.Marshal(cxml)
	if err != nil {
		return out, err
	}
	buffer := new(bytes.Buffer)
	buffer.Write(bs)

	params := "uploadId=" + imur.UploadID
	resp, err := bucket.do("POST", imur.Key, params, params, nil, buffer, nil)
	if err != nil {
		return out, err
	}
	defer resp.Body.Close()

	err = xmlUnmarshal(resp.Body, &out)
	return out, err
}

//
// AbortMultipartUpload 取消分片上传任务。
//
// imur  InitiateMultipartUpload的返回值。
//
// error  操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) AbortMultipartUpload(imur InitiateMultipartUploadResult) error {
	params := "uploadId=" + imur.UploadID
	resp, err := bucket.do("DELETE", imur.Key, params, params, nil, nil, nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
}

//
// ListUploadedParts 列出指定上传任务已经上传的分片。
//
// imur  InitiateMultipartUpload的返回值。
//
// ListUploadedPartsResponse  操作成功后的返回值,成员UploadedParts已经上传/拷贝的片。error为nil时该返回值有效。
// error  操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) ListUploadedParts(imur InitiateMultipartUploadResult) (ListUploadedPartsResult, error) {
	var out ListUploadedPartsResult
	params := "uploadId=" + imur.UploadID
	resp, err := bucket.do("GET", imur.Key, params, params, nil, nil, nil)
	if err != nil {
		return out, err
	}
	defer resp.Body.Close()

	err = xmlUnmarshal(resp.Body, &out)
	return out, err
}

//
// ListMultipartUploads 列出所有未上传完整的multipart任务列表。
//
// options  ListObject的筛选行为。Prefix返回object的前缀,KeyMarker返回object的起始位置,MaxUploads最大数目默认1000,
// Delimiter用于对Object名字进行分组的字符,所有名字包含指定的前缀且第一次出现delimiter字符之间的object。
//
// ListMultipartUploadResponse  操作成功后的返回值,error为nil时该返回值有效。
// error  操作成功error为nil,非nil为错误信息。
//
func (bucket Bucket) ListMultipartUploads(options ...Option) (ListMultipartUploadResult, error) {
	var out ListMultipartUploadResult

	options = append(options, EncodingType("url"))
	params, err := handleParams(options)
	if err != nil {
		return out, err
	}

	resp, err := bucket.do("GET", "", "uploads&"+params, "uploads", nil, nil, nil)
	if err != nil {
		return out, err
	}
	defer resp.Body.Close()

	err = xmlUnmarshal(resp.Body, &out)
	if err != nil {
		return out, err
	}
	err = decodeListMultipartUploadResult(&out)
	return out, err
}