1package cos
2
3import (
4	"context"
5	"encoding/xml"
6	"fmt"
7	"io"
8	"net/http"
9	"net/url"
10)
11
12// ObjectService ...
13//
14// Object 相关 API
15type ObjectService service
16
17// ObjectGetOptions ...
18//
19// https://cloud.tencent.com/document/product/436/7753
20type ObjectGetOptions struct {
21	// 设置响应头部中的 Content-Type 参数
22	ResponseContentType string `url:"response-content-type,omitempty" header:"-"`
23	// 设置响应头部中的 Content-Language 参数
24	ResponseContentLanguage string `url:"response-content-language,omitempty" header:"-"`
25	// 设置响应头部中的 Content-Expires 参数
26	ResponseExpires string `url:"response-expires,omitempty" header:"-"`
27	// 设置响应头部中的 Cache-Control 参数
28	ResponseCacheControl string `url:"response-cache-control,omitempty" header:"-"`
29	// 设置响应头部中的 Content-Disposition 参数
30	ResponseContentDisposition string `url:"response-content-disposition,omitempty" header:"-"`
31	// 设置响应头部中的 Content-Encoding 参数
32	ResponseContentEncoding string `url:"response-content-encoding,omitempty" header:"-"`
33	// RFC 2616 中定义的指定文件下载范围,以字节(bytes)为单位
34	Range string `url:"-" header:"Range,omitempty"`
35	// 如果文件修改时间早于或等于指定时间,才返回文件内容。否则返回 412 (precondition failed)
36	IfUnmodifiedSince string `url:"-" header:"If-Unmodified-Since,omitempty"`
37	// 当 Object 在指定时间后被修改,则返回对应 Object meta 信息,否则返回 304(not modified)
38	IfModifiedSince string `url:"-" header:"If-Modified-Since,omitempty"`
39	// 当 ETag 与指定的内容一致,才返回文件。否则返回 412 (precondition failed)
40	IfMatch string `url:"-" header:"If-Match,omitempty"`
41	// 当 ETag 与指定的内容不一致,才返回文件。否则返回 304 (not modified)
42	IfNoneMatch string `url:"-" header:"If-None-Match,omitempty"`
43
44	// 预签名授权 URL
45	PresignedURL *url.URL `header:"-" url:"-" xml:"-"`
46}
47
48// MethodObjectGet method name of Object.Get
49const MethodObjectGet MethodName = "Object.Get"
50
51// Get Object 请接口请求可以在 COS 的存储桶中将一个文件(对象)下载至本地。
52// 该操作需要请求者对目标对象具有读权限或目标对象对所有人都开放了读权限(公有读)。
53//
54// 版本
55//
56// 当启用多版本,该 GET 操作返回对象的当前版本。要返回不同的版本,请使用 versionId 参数。
57//
58// 注意
59//
60// 如果该对象的当前版本是删除标记,则 COS 的行为表现为该对象不存在,并返回响应 x-cos-delete-marker: true。
61//
62// https://cloud.tencent.com/document/product/436/7753
63func (s *ObjectService) Get(ctx context.Context, name string, opt *ObjectGetOptions) (*Response, error) {
64	baseURL := s.client.BaseURL.BucketURL
65	uri := "/" + encodeURIComponent(name)
66	if opt != nil && opt.PresignedURL != nil {
67		baseURL = opt.PresignedURL
68		uri = ""
69	}
70	sendOpt := sendOptions{
71		baseURL:          baseURL,
72		uri:              uri,
73		method:           http.MethodGet,
74		optQuery:         opt,
75		optHeader:        opt,
76		disableCloseBody: true,
77		caller: Caller{
78			Method: MethodObjectGet,
79		},
80	}
81	resp, err := s.client.send(ctx, &sendOpt)
82	return resp, err
83}
84
85// ObjectPutHeaderOptions ...
86type ObjectPutHeaderOptions struct {
87	// RFC 2616 中定义的缓存策略,将作为 Object 元数据保存。
88	CacheControl string `header:"Cache-Control,omitempty" url:"-"`
89	// RFC 2616 中定义的文件名称,将作为 Object 元数据保存。
90	ContentDisposition string `header:"Content-Disposition,omitempty" url:"-"`
91	// RFC 2616 中定义的编码格式,将作为 Object 元数据保存。
92	ContentEncoding string `header:"Content-Encoding,omitempty" url:"-"`
93	// RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存。
94	ContentType string `header:"Content-Type,omitempty" url:"-"`
95	//
96	ContentLength int `header:"Content-Length,omitempty" url:"-"`
97	// RFC 2616 中定义的文件日期和时间,将作为 Object 元数据保存。
98	Expect          string `header:"Expect,omitempty" url:"-"`
99	Expires         string `header:"Expires,omitempty" url:"-"`
100	XCosContentSHA1 string `header:"x-cos-content-sha1,omitempty" url:"-"`
101	// 自定义的 x-cos-meta-* header
102	// 包括用户自定义头部后缀和用户自定义头部信息,将作为 Object 元数据返回,大小限制为 2KB。
103	// 注意:用户自定义头部信息支持下划线,但用户自定义头部后缀不支持下划线。
104	XCosMetaXXX *http.Header `header:"x-cos-meta-*,omitempty" url:"-"`
105	// 设置 Object 的存储级别,枚举值:STANDARD, STANDARD_IA,默认值:STANDARD
106	XCosStorageClass string `header:"x-cos-storage-class,omitempty" url:"-"`
107
108	// XCosServerSideEncryption 用于指定腾讯云 COS 在数据存储时,应用数据加密的保护策略。
109	// 腾讯云 COS 会帮助您在数据写入数据中心时自动加密,并在您取用该数据时自动解密。
110	// 目前支持使用腾讯云 COS 主密钥对数据进行 AES-256 加密。
111	// 如果您需要对数据启用服务端加密,则需指定 XCosServerSideEncryption。
112	//
113	// 指定将对象启用服务端加密的方式。使用 COS 主密钥加密填写:AES256
114	XCosServerSideEncryption string `header:"x-cos-server-side-encryption,omitempty" url:"-"`
115	// 可选值: Normal, Appendable
116	//XCosObjectType string `header:"x-cos-object-type,omitempty" url:"-"`
117}
118
119// ObjectPutOptions ...
120type ObjectPutOptions struct {
121	*ACLHeaderOptions       `header:",omitempty" url:"-" xml:"-"`
122	*ObjectPutHeaderOptions `header:",omitempty" url:"-" xml:"-"`
123
124	// 预签名授权 URL
125	PresignedURL *url.URL `header:"-" url:"-" xml:"-"`
126}
127
128// MethodObjectPut method name of Object.Put
129const MethodObjectPut MethodName = "Object.Put"
130
131// Put Object请求可以将一个文件(Object)上传至指定Bucket。
132//
133// 版本
134//
135// * 如果对存储桶启用版本控制,对象存储将自动为要添加的对象生成唯一的版本 ID。
136//   对象存储使用 x-cos-version-id 响应头部在响应中返回此标识。
137// * 如果需要暂停存储桶的版本控制,则对象存储始终将其 null 用作存储在存储桶中的对象的版本 ID。
138//
139// 细节分析
140//
141// * 需要有 Bucket 的写权限;
142// * 如果请求头的 Content-Length 值小于实际请求体(body)中传输的数据长度,COS 仍将成功创建文件,
143//   但 Object 大小只等于 Content-Length 中定义的大小,其他数据将被丢弃;
144// * 如果试图添加的 Object 的同名文件已经存在,那么新上传的文件,将覆盖原来的文件,成功时返回 200 OK。
145//
146// 当 r 是个 io.ReadCloser 时 Put 方法不会自动调用 r.Close(),用户需要自行选择合适的时机去调用 r.Close() 方法对 r 进行资源回收
147//
148// https://cloud.tencent.com/document/product/436/7749
149func (s *ObjectService) Put(ctx context.Context, name string, r io.Reader, opt *ObjectPutOptions) (*Response, error) {
150	baseURL := s.client.BaseURL.BucketURL
151	uri := "/" + encodeURIComponent(name)
152	if opt != nil && opt.PresignedURL != nil {
153		baseURL = opt.PresignedURL
154		uri = ""
155	}
156	sendOpt := sendOptions{
157		baseURL:   baseURL,
158		uri:       uri,
159		method:    http.MethodPut,
160		body:      r,
161		optHeader: opt,
162		caller: Caller{
163			Method: MethodObjectPut,
164		},
165	}
166	resp, err := s.client.send(ctx, &sendOpt)
167	return resp, err
168}
169
170// ObjectCopyHeaderOptions ...
171// https://cloud.tencent.com/document/product/436/10881
172type ObjectCopyHeaderOptions struct {
173	// 是否拷贝源文件的元数据,枚举值:Copy, Replaced,默认值 Copy。假如标记为 Copy,
174	// 则拷贝源文件的元数据;假如标记为 Replaced,则按本次请求的 Header 信息修改元数据。
175	// 当目标路径和源路径一致,即用户试图修改元数据时,则标记必须为 Replaced。
176	XCosMetadataDirective string `header:"x-cos-metadata-directive,omitempty" url:"-" xml:"-"`
177	// 当 Object 在指定时间后被修改,则执行操作,否则返回 412。
178	// 可与 XCosCopySourceIfNoneMatch 一起使用,与其他条件联合使用返回冲突。
179	XCosCopySourceIfModifiedSince string `header:"x-cos-copy-source-If-Modified-Since,omitempty" url:"-" xml:"-"`
180	// 当 Object 在指定时间后未被修改,则执行操作,否则返回 412。
181	// 可与 XCosCopySourceIfMatch 一起使用,与其他条件联合使用返回冲突。
182	XCosCopySourceIfUnmodifiedSince string `header:"x-cos-copy-source-If-Unmodified-Since,omitempty" url:"-" xml:"-"`
183	// 当 Object 的 Etag 和给定一致时,则执行操作,否则返回 412。
184	// 可与 XCosCopySourceIfUnmodifiedSince 一起使用,与其他条件联合使用返回冲突。
185	XCosCopySourceIfMatch string `header:"x-cos-copy-source-If-Match,omitempty" url:"-" xml:"-"`
186	// 当 Object 的 Etag 和给定不一致时,则执行操作,否则返回 412。
187	// 可与 XCosCopySourceIfModifiedSince 一起使用,与其他条件联合使用返回冲突。
188	XCosCopySourceIfNoneMatch string `header:"x-cos-copy-source-If-None-Match,omitempty" url:"-" xml:"-"`
189	// 设置 Object 的存储级别,枚举值:STANDARD,STANDARD_IA。默认值:STANDARD
190	XCosStorageClass string `header:"x-cos-storage-class,omitempty" url:"-" xml:"-"`
191	// 自定义的 x-cos-meta-* header
192	XCosMetaXXX *http.Header `header:"x-cos-meta-*,omitempty" url:"-"`
193	// 源文件 URL 路径,可以通过 versionid 子资源指定历史版本
194	XCosCopySource string `header:"x-cos-copy-source" url:"-" xml:"-"`
195
196	// XCosServerSideEncryption 用于指定腾讯云 COS 在数据存储时,应用数据加密的保护策略。
197	// 腾讯云 COS 会帮助您在数据写入数据中心时自动加密,并在您取用该数据时自动解密。
198	// 目前支持使用腾讯云 COS 主密钥对数据进行 AES-256 加密。
199	// 如果您需要对数据启用服务端加密,则需指定 XCosServerSideEncryption。
200	//
201	// 指定将对象启用服务端加密的方式。使用 COS 主密钥加密填写:AES256
202	XCosServerSideEncryption string `header:"x-cos-server-side-encryption,omitempty" url:"-"`
203}
204
205// ObjectCopyOptions ...
206//
207// https://cloud.tencent.com/document/product/436/10881
208type ObjectCopyOptions struct {
209	*ObjectCopyHeaderOptions `header:",omitempty" url:"-" xml:"-"`
210	*ACLHeaderOptions        `header:",omitempty" url:"-" xml:"-"`
211}
212
213// ObjectCopyResult ...
214type ObjectCopyResult struct {
215	XMLName xml.Name `xml:"CopyObjectResult"`
216	// 返回文件的 MD5 算法校验值。ETag 的值可以用于检查 Object 的内容是否发生变化。
217	ETag string `xml:"ETag,omitempty"`
218	// 返回文件最后修改时间,GMT 格式
219	LastModified string `xml:"LastModified,omitempty"`
220}
221
222// MethodObjectCopy method name of Object.Copy
223const MethodObjectCopy MethodName = "Object.Copy"
224
225// Copy ...
226//
227// Put Object Copy 请求实现将一个文件从源路径复制到目标路径。建议文件大小 1M 到 5G,
228// 超过 5G 的文件请使用分块上传 Upload - Copy。在拷贝的过程中,文件元属性和 ACL 可以被修改。
229//
230// 用户可以通过该接口实现文件移动,文件重命名,修改文件属性和创建副本。
231//
232// 版本
233//
234// * 默认情况下,在目标存储桶上启用版本控制,对象存储会为正在复制的对象生成唯一的版本 ID。此版本 ID 与源对象的版本 ID 不同。
235//   对象存储会在 x-cos-version-id 响应中的响应标头中返回复制对象的版本 ID。
236// * 如果您在目标存储桶没有启用版本控制或暂停版本控制,则对象存储生成的版本 ID 始终为 null。
237//
238// 注意:在跨帐号复制的时候,需要先设置被复制文件的权限为公有读,或者对目标帐号赋权,同帐号则不需要。
239//
240// https://cloud.tencent.com/document/product/436/10881
241func (s *ObjectService) Copy(ctx context.Context, name, sourceURL string, opt *ObjectCopyOptions) (*ObjectCopyResult, *Response, error) {
242	var res ObjectCopyResult
243	if opt == nil {
244		opt = new(ObjectCopyOptions)
245	}
246	if opt.ObjectCopyHeaderOptions == nil {
247		opt.ObjectCopyHeaderOptions = new(ObjectCopyHeaderOptions)
248	}
249	opt.XCosCopySource = sourceURL
250
251	sendOpt := sendOptions{
252		baseURL:   s.client.BaseURL.BucketURL,
253		uri:       "/" + encodeURIComponent(name),
254		method:    http.MethodPut,
255		body:      nil,
256		optHeader: opt,
257		result:    &res,
258		caller: Caller{
259			Method: MethodObjectCopy,
260		},
261	}
262	resp, err := s.client.send(ctx, &sendOpt)
263	return &res, resp, err
264}
265
266// MethodObjectDelete method name of Object.Delete
267const MethodObjectDelete MethodName = "Object.Delete"
268
269// Delete Object 接口请求可以在 COS 的 Bucket 中将一个文件(Object)删除。该操作需要请求者对 Bucket 有 WRITE 权限。
270//
271// 细节分析
272//
273// * 在 DELETE Object 请求中删除一个不存在的 Object,仍然认为是成功的,返回 204 No Content。
274// * DELETE Object 要求用户对该 Object 要有写权限。
275//
276// https://cloud.tencent.com/document/product/436/7743
277func (s *ObjectService) Delete(ctx context.Context, name string) (*Response, error) {
278	sendOpt := sendOptions{
279		baseURL: s.client.BaseURL.BucketURL,
280		uri:     "/" + encodeURIComponent(name),
281		method:  http.MethodDelete,
282		caller: Caller{
283			Method: MethodObjectDelete,
284		},
285	}
286	resp, err := s.client.send(ctx, &sendOpt)
287	return resp, err
288}
289
290// ObjectHeadOptions ...
291type ObjectHeadOptions struct {
292	// 当 Object 在指定时间后被修改,则返回对应 Object 的 meta 信息,否则返回 304
293	IfModifiedSince string `url:"-" header:"If-Modified-Since,omitempty"`
294}
295
296// MethodObjectHead method name of Object.Head
297const MethodObjectHead MethodName = "Object.Head"
298
299// Head Object请求可以取回对应Object的元数据,Head的权限与Get的权限一致
300//
301// 默认情况下,HEAD 操作从当前版本的对象中检索元数据。如要从不同版本检索元数据,请使用 versionId 子资源。
302//
303// https://cloud.tencent.com/document/product/436/7745
304func (s *ObjectService) Head(ctx context.Context, name string, opt *ObjectHeadOptions) (*Response, error) {
305	sendOpt := sendOptions{
306		baseURL:   s.client.BaseURL.BucketURL,
307		uri:       "/" + encodeURIComponent(name),
308		method:    http.MethodHead,
309		optHeader: opt,
310		caller: Caller{
311			Method: MethodObjectHead,
312		},
313	}
314	resp, err := s.client.send(ctx, &sendOpt)
315	return resp, err
316}
317
318// ObjectOptionsOptions ...
319//
320// https://cloud.tencent.com/document/product/436/8288
321type ObjectOptionsOptions struct {
322	// 模拟跨域访问的请求来源域名,必选
323	Origin string `url:"-" header:"Origin"`
324	// 模拟跨域访问的请求 HTTP 方法,必选
325	AccessControlRequestMethod string `url:"-" header:"Access-Control-Request-Method"`
326	// 模拟跨域访问的请求头部
327	AccessControlRequestHeaders string `url:"-" header:"Access-Control-Request-Headers,omitempty"`
328}
329
330// MethodObjectOptions method name of Object.Options
331const MethodObjectOptions MethodName = "Object.Options"
332
333// Options Object 接口实现 Object 跨域访问配置的预请求。
334// 即在发送跨域请求之前会发送一个 OPTIONS 请求并带上特定的来源域,HTTP 方法和 Header 信息等给 COS,
335// 以决定是否可以发送真正的跨域请求。当 CORS 配置不存在时,请求返回 403 Forbidden。
336// 可以通过 PUT Bucket cors 接口来开启 Bucket 的 CORS 支持。
337//
338// https://cloud.tencent.com/document/product/436/8288
339func (s *ObjectService) Options(ctx context.Context, name string, opt *ObjectOptionsOptions) (*Response, error) {
340	sendOpt := sendOptions{
341		baseURL:   s.client.BaseURL.BucketURL,
342		uri:       "/" + encodeURIComponent(name),
343		method:    http.MethodOptions,
344		optHeader: opt,
345		caller: Caller{
346			Method: MethodObjectOptions,
347		},
348	}
349	resp, err := s.client.send(ctx, &sendOpt)
350	return resp, err
351}
352
353// MethodObjectAppend method name of Object.Append
354const MethodObjectAppend MethodName = "Object.Append"
355
356// Append ...
357//
358// Append请求可以将一个文件(Object)以分块追加的方式上传至 Bucket 中。使用Append Upload的文件必须事前被设定为Appendable。
359// 当Appendable的文件被执行Put Object的操作以后,文件被覆盖,属性改变为Normal。
360//
361// 文件属性可以在Head Object操作中被查询到,当您发起Head Object请求时,会返回自定义Header『x-cos-object-type』,该Header只有两个枚举值:Normal或者Appendable。
362//
363// 追加上传建议文件大小1M - 5G。如果position的值和当前Object的长度不致,COS会返回409错误。
364// 如果Append一个Normal的Object,COS会返回409 ObjectNotAppendable。
365//
366// Appendable的文件不可以被复制,不参与版本管理,不参与生命周期管理,不可跨区域复制。
367//
368// 当 r 不是 bytes.Buffer/bytes.Reader/strings.Reader 时,必须指定 opt.ObjectPutHeaderOptions.ContentLength
369// 当 r 是个 io.ReadCloser 时 Append 方法不会自动调用 r.Close(),用户需要自行选择合适的时机去调用 r.Close() 方法对 r 进行资源回收
370//
371// https://www.qcloud.com/document/product/436/7741
372func (s *ObjectService) Append(ctx context.Context, name string, position int, r io.Reader, opt *ObjectPutOptions) (*Response, error) {
373	u := fmt.Sprintf("/%s?append&position=%d", encodeURIComponent(name), position)
374	sendOpt := sendOptions{
375		baseURL:   s.client.BaseURL.BucketURL,
376		uri:       u,
377		method:    http.MethodPost,
378		optHeader: opt,
379		body:      r,
380		caller: Caller{
381			Method: MethodObjectAppend,
382		},
383	}
384	resp, err := s.client.send(ctx, &sendOpt)
385	return resp, err
386}
387
388// ObjectDeleteMultiOptions ...
389//
390// https://cloud.tencent.com/document/product/436/8289
391type ObjectDeleteMultiOptions struct {
392	XMLName xml.Name `xml:"Delete" header:"-"`
393	// 布尔值,这个值决定了是否启动 Quiet 模式。
394	// 值为 true 启动 Quiet 模式,值为 false 则启动 Verbose 模式,默认值为 False
395	Quiet bool `xml:"Quiet" header:"-"`
396	// 说明每个将要删除的目标 Object 信息,只需指定 Key 即可
397	Objects []Object `xml:"Object" header:"-"`
398	//XCosSha1 string `xml:"-" header:"x-cos-sha1"`
399}
400
401// ObjectDeleteMultiResult ...
402//
403// https://cloud.tencent.com/document/product/436/8289
404type ObjectDeleteMultiResult struct {
405	XMLName xml.Name `xml:"DeleteResult"`
406	// 说明本次删除的成功 Object 信息
407	DeletedObjects []Object `xml:"Deleted,omitempty"`
408	// 说明本次删除的失败 Object 信息
409	Errors []struct {
410		// 删除失败的 Object 的名称
411		Key string
412		// 删除失败的错误代码
413		Code string
414		// 删除失败的错误信息
415		Message string
416	} `xml:"Error,omitempty"`
417}
418
419// MethodObjectDeleteMulti method name of Object.DeleteMulti
420const MethodObjectDeleteMulti MethodName = "Object.DeleteMulti"
421
422// DeleteMulti ...
423//
424// Delete Multiple Object请求实现批量删除文件,最大支持单次删除1000个文件。
425// 对于返回结果,COS提供Verbose和Quiet两种结果模式。Verbose模式将返回每个Object的删除结果;
426// Quiet模式只返回报错的Object信息。
427//
428// 细节分析
429//
430// * 每一个批量删除请求,最多只能包含 1000个 需要删除的对象;
431// * 批量删除支持二种模式的放回,verbose 模式和 quiet 模式,默认为 verbose 模式。
432//   verbose 模式返回每个 key 的删除情况,quiet 模式只返回删除失败的 key 的情况;
433// * 批量删除需要携带 Content-MD5 头部,用以校验请求 body 没有被修改;
434// * 批量删除请求允许删除一个不存在的 key,仍然认为成功。
435//
436// https://cloud.tencent.com/document/product/436/8289
437func (s *ObjectService) DeleteMulti(ctx context.Context, opt *ObjectDeleteMultiOptions) (*ObjectDeleteMultiResult, *Response, error) {
438	var res ObjectDeleteMultiResult
439	sendOpt := sendOptions{
440		baseURL: s.client.BaseURL.BucketURL,
441		uri:     "/?delete",
442		method:  http.MethodPost,
443		body:    opt,
444		result:  &res,
445		caller: Caller{
446			Method: MethodObjectDeleteMulti,
447		},
448	}
449	resp, err := s.client.send(ctx, &sendOpt)
450	return &res, resp, err
451}
452
453type objectPresignedURLTestingOptions struct {
454	authTime *AuthTime
455}
456
457// PresignedURL 生成预签名授权 URL,可用于无需知道 SecretID 和 SecretKey 就可以上传和下载文件 。
458//
459// httpMethod:
460//   * 下载文件:http.MethodGet
461//   * 上传文件: http.MethodPut
462//
463// 下载文件 时 opt 可以是 *ObjectGetOptions ,上传文件时 opt 可以是 *ObjectPutOptions
464//
465// https://cloud.tencent.com/document/product/436/14116
466// https://cloud.tencent.com/document/product/436/14114
467func (s *ObjectService) PresignedURL(ctx context.Context, httpMethod, name string, auth Auth, opt interface{}) (*url.URL, error) {
468	sendOpt := sendOptions{
469		baseURL:   s.client.BaseURL.BucketURL,
470		uri:       "/" + encodeURIComponent(name),
471		method:    httpMethod,
472		optQuery:  opt,
473		optHeader: opt,
474	}
475	req, err := s.client.newRequest(ctx, &sendOpt)
476	if err != nil {
477		return nil, err
478	}
479
480	var authTime *AuthTime
481	if opt != nil {
482		if opt, ok := opt.(*objectPresignedURLTestingOptions); ok {
483			authTime = opt.authTime
484		}
485	}
486	if authTime == nil {
487		authTime = NewAuthTime(auth.Expire)
488	}
489	authorization := newAuthorization(auth, req, *authTime)
490	sign := encodeURIComponent(authorization)
491
492	if req.URL.RawQuery == "" {
493		req.URL.RawQuery = fmt.Sprintf("sign=%s", sign)
494	} else {
495		req.URL.RawQuery = fmt.Sprintf("%s&sign=%s", req.URL.RawQuery, sign)
496	}
497	return req.URL, nil
498}
499
500// Object ...
501type Object struct {
502	// Object 的 Key
503	Key string `xml:",omitempty"`
504	// 文件的 MD-5 算法校验值
505	ETag string `xml:",omitempty"`
506	// 说明文件大小,单位是 Byte
507	Size int `xml:",omitempty"`
508	// 块编号
509	PartNumber int `xml:",omitempty"`
510	// 说明 Object 最后被修改时间
511	LastModified string `xml:",omitempty"`
512	// Object 的存储级别,枚举值:STANDARD,STANDARD_IA,ARCHIVE
513	StorageClass string `xml:",omitempty"`
514	// Bucket 持有者信息
515	Owner *Owner `xml:",omitempty"`
516}
517