bucket.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. package oss
  2. import (
  3. "bytes"
  4. "crypto/md5"
  5. "encoding/base64"
  6. "encoding/xml"
  7. "hash"
  8. "hash/crc64"
  9. "io"
  10. "io/ioutil"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. "strconv"
  15. )
  16. // Bucket implements the operations of object.
  17. type Bucket struct {
  18. Client Client
  19. BucketName string
  20. }
  21. //
  22. // PutObject 新建Object,如果Object已存在,覆盖原有Object。
  23. //
  24. // objectKey 上传对象的名称,使用UTF-8编码、长度必须在1-1023字节之间、不能以“/”或者“\”字符开头。
  25. // reader io.Reader读取object的数据。
  26. // options 上传对象时可以指定对象的属性,可用选项有CacheControl、ContentDisposition、ContentEncoding、
  27. // Expires、ServerSideEncryption、ObjectACL、Meta,具体含义请参看
  28. // https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
  29. //
  30. // error 操作无错误为nil,非nil为错误信息。
  31. //
  32. func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
  33. opts := addContentType(options, objectKey)
  34. resp, err := bucket.do("PUT", objectKey, "", "", opts, reader)
  35. if err != nil {
  36. return err
  37. }
  38. defer resp.body.Close()
  39. if bucket.getConfig().IsEnableCRC {
  40. err = checkCRC(resp, "PutObject")
  41. if err != nil {
  42. return err
  43. }
  44. }
  45. return checkRespCode(resp.statusCode, []int{http.StatusOK})
  46. }
  47. //
  48. // PutObjectFromFile 新建Object,内容从本地文件中读取。
  49. //
  50. // objectKey 上传对象的名称。
  51. // filePath 本地文件,上传对象的值为该文件内容。
  52. // options 上传对象时可以指定对象的属性。详见PutObject的options。
  53. //
  54. // error 操作无错误为nil,非nil为错误信息。
  55. //
  56. func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Option) error {
  57. fd, err := os.Open(filePath)
  58. if err != nil {
  59. return err
  60. }
  61. defer fd.Close()
  62. opts := addContentType(options, filePath, objectKey)
  63. resp, err := bucket.do("PUT", objectKey, "", "", opts, fd)
  64. if err != nil {
  65. return err
  66. }
  67. defer resp.body.Close()
  68. if bucket.getConfig().IsEnableCRC {
  69. err = checkCRC(resp, "PutObjectFromFile")
  70. if err != nil {
  71. return err
  72. }
  73. }
  74. return checkRespCode(resp.statusCode, []int{http.StatusOK})
  75. }
  76. //
  77. // GetObject 下载文件。
  78. //
  79. // objectKey 下载的文件名称。
  80. // options 对象的属性限制项,可选值有Range、IfModifiedSince、IfUnmodifiedSince、IfMatch、
  81. // IfNoneMatch、AcceptEncoding,详细请参考
  82. // https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
  83. //
  84. // io.ReadCloser reader,读取数据后需要close。error为nil时有效。
  85. // error 操作无错误为nil,非nil为错误信息。
  86. //
  87. func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
  88. resp, err := bucket.do("GET", objectKey, "", "", options, nil)
  89. if err != nil {
  90. return nil, err
  91. }
  92. return resp.body, nil
  93. }
  94. //
  95. // GetObjectWithCRC 下载文件同时计算CRC。
  96. //
  97. // objectKey 下载的文件名称。
  98. // options 对象的属性限制项,可选值有Range、IfModifiedSince、IfUnmodifiedSince、IfMatch、
  99. // IfNoneMatch、AcceptEncoding,详细请参考
  100. // https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
  101. //
  102. // io.ReadCloser body,文件内容,读取数据后需要close。error为nil时有效。
  103. // hash.Hash64 clientCRCCalculator,读取数据的CRC计算器。数据读取介绍通过clientCRCCalculator.Sum64()获取。
  104. // int64 serverCRC 服务器端存储数据的CRC值。
  105. // error 操作无错误为nil,非nil为错误信息。
  106. //
  107. func (bucket Bucket) GetObjectWithCRC(objectKey string, options ...Option) (body io.ReadCloser, clientCRCCalculator hash.Hash64, serverCRC uint64, err error) {
  108. resp, err := bucket.do("GET", objectKey, "", "", options, nil)
  109. if err != nil {
  110. return
  111. }
  112. body = resp.body
  113. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  114. if bucket.getConfig().IsEnableCRC && !hasRange {
  115. serverCRC = resp.serverCRC
  116. clientCRCCalculator = crc64.New(crcTable())
  117. body = ioutil.NopCloser(io.TeeReader(body, clientCRCCalculator))
  118. }
  119. return
  120. }
  121. //
  122. // GetObjectToFile 下载文件。
  123. //
  124. // objectKey 下载的文件名称。
  125. // filePath 下载对象的内容写到该本地文件。
  126. // options 对象的属性限制项。详见GetObject的options。
  127. //
  128. // error 操作无错误时返回error为nil,非nil为错误说明。
  129. //
  130. func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
  131. resp, err := bucket.do("GET", objectKey, "", "", options, nil)
  132. if err != nil {
  133. return err
  134. }
  135. defer resp.body.Close()
  136. // 如果文件不存在则创建,存在则清空
  137. fd, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
  138. if err != nil {
  139. return err
  140. }
  141. defer fd.Close()
  142. // 读取数据同时计算CRC
  143. body := resp.body
  144. var crc hash.Hash64
  145. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  146. if bucket.getConfig().IsEnableCRC && !hasRange {
  147. crc = crc64.New(crcTable())
  148. body = ioutil.NopCloser(io.TeeReader(body, crc))
  149. }
  150. _, err = io.Copy(fd, body)
  151. if err != nil {
  152. return err
  153. }
  154. // 比较CRC值
  155. if bucket.getConfig().IsEnableCRC && !hasRange {
  156. resp.clientCRC = crc.Sum64()
  157. err = checkCRC(resp, "GetObjectToFile")
  158. if err != nil {
  159. return err
  160. }
  161. }
  162. return nil
  163. }
  164. //
  165. // CopyObject 同一个bucket内拷贝Object。
  166. //
  167. // srcObjectKey Copy的源对象。
  168. // destObjectKey Copy的目标对象。
  169. // options Copy对象时,您可以指定源对象的限制条件,满足限制条件时copy,不满足时返回错误,您可以选择如下选项CopySourceIfMatch、
  170. // CopySourceIfNoneMatch、CopySourceIfModifiedSince、CopySourceIfUnmodifiedSince、MetadataDirective。
  171. // Copy对象时,您可以指定目标对象的属性,如CacheControl、ContentDisposition、ContentEncoding、Expires、
  172. // ServerSideEncryption、ObjectACL、Meta,选项的含义请参看
  173. // https://help.aliyun.com/document_detail/oss/api-reference/object/CopyObject.html
  174. //
  175. // error 操作无错误为nil,非nil为错误信息。
  176. //
  177. func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  178. var out CopyObjectResult
  179. options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
  180. resp, err := bucket.do("PUT", destObjectKey, "", "", options, nil)
  181. if err != nil {
  182. return out, err
  183. }
  184. defer resp.body.Close()
  185. err = xmlUnmarshal(resp.body, &out)
  186. return out, err
  187. }
  188. //
  189. // CopyObjectTo bucket间拷贝object。
  190. //
  191. // srcObjectKey 源Object名称。源Bucket名称为Bucket.BucketName。
  192. // destBucketName 目标Bucket名称。
  193. // destObjectKey 目标Object名称。
  194. // options Copy选项,详见CopyObject的options。
  195. //
  196. // error 操作无错误为nil,非nil为错误信息。
  197. //
  198. func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
  199. return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
  200. }
  201. //
  202. // CopyObjectFrom bucket间拷贝object。
  203. //
  204. // srcBucketName 源Bucket名称。
  205. // srcObjectKey 源Object名称。
  206. // destObjectKey 目标Object名称。目标Bucket名称为Bucket.BucketName。
  207. // options Copy选项,详见CopyObject的options。
  208. //
  209. // error 操作无错误为nil,非nil为错误信息。
  210. //
  211. func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  212. destBucketName := bucket.BucketName
  213. var out CopyObjectResult
  214. srcBucket, err := bucket.Client.Bucket(srcBucketName)
  215. if err != nil {
  216. return out, err
  217. }
  218. return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
  219. }
  220. func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  221. var out CopyObjectResult
  222. options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
  223. headers := make(map[string]string)
  224. err := handleOptions(headers, options)
  225. if err != nil {
  226. return out, err
  227. }
  228. resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, "", "", headers, nil, 0)
  229. if err != nil {
  230. return out, err
  231. }
  232. defer resp.body.Close()
  233. err = xmlUnmarshal(resp.body, &out)
  234. return out, err
  235. }
  236. //
  237. // AppendObject 追加方式上传。
  238. //
  239. // AppendObject参数必须包含position,其值指定从何处进行追加。首次追加操作的position必须为0,
  240. // 后续追加操作的position是Object的当前长度。例如,第一次Append Object请求指定position值为0,
  241. // content-length是65536;那么,第二次Append Object需要指定position为65536。
  242. // 每次操作成功后,响应头部x-oss-next-append-position也会标明下一次追加的position。
  243. //
  244. // objectKey 需要追加的Object。
  245. // reader io.Reader,读取追的内容。
  246. // appendPosition object追加的起始位置。
  247. // destObjectProperties 第一次追加时指定新对象的属性,如CacheControl、ContentDisposition、ContentEncoding、
  248. // Expires、ServerSideEncryption、ObjectACL。
  249. //
  250. // int64 下次追加的开始位置,error为nil空时有效。
  251. // error 操作无错误为nil,非nil为错误信息。
  252. //
  253. func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
  254. var nextAppendPosition int64
  255. params := "append&position=" + strconv.Itoa(int(appendPosition))
  256. opts := addContentType(options, objectKey)
  257. resp, err := bucket.do("POST", objectKey, params, params, opts, reader)
  258. if err != nil {
  259. return appendPosition, err
  260. }
  261. defer resp.body.Close()
  262. nextAppendPosition, err = strconv.ParseInt(resp.headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
  263. if err != nil {
  264. return nextAppendPosition, err
  265. }
  266. return nextAppendPosition, nil
  267. }
  268. //
  269. // AppendObjectWithCRC 追加方式上传。
  270. //
  271. // AppendObject参数必须包含position,其值指定从何处进行追加。首次追加操作的position必须为0,
  272. // 后续追加操作的position是Object的当前长度。例如,第一次Append Object请求指定position值为0,
  273. // content-length是65536;那么,第二次Append Object需要指定position为65536。
  274. // 每次操作成功后,响应头部x-oss-next-append-position也会标明下一次追加的position。
  275. //
  276. // objectKey 需要追加的Object。
  277. // reader io.Reader,读取追的内容。
  278. // appendPosition object追加的起始位置。
  279. // initCRC 上次追加的返回的CRC校验值,第一次填0。
  280. // destObjectProperties 第一次追加时指定新对象的属性,如CacheControl、ContentDisposition、ContentEncoding、
  281. // Expires、ServerSideEncryption、ObjectACL。
  282. //
  283. // int64 下次追加的开始位置,error为nil空时有效。
  284. // int64 本次追加后文件的CRC值,作为下次的初始化值。
  285. // error 操作无错误为nil,非nil为错误信息。
  286. //
  287. func (bucket Bucket) AppendObjectWithCRC(objectKey string, reader io.Reader, appendPosition int64, initCRC uint64, options ...Option) (int64, uint64, error) {
  288. var nextAppendPosition int64
  289. params := "append&position=" + strconv.Itoa(int(appendPosition))
  290. opts := addContentType(options, objectKey)
  291. headers := make(map[string]string)
  292. handleOptions(headers, opts)
  293. resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, objectKey, params, params, headers, reader, initCRC)
  294. if err != nil {
  295. return nextAppendPosition, initCRC, err
  296. }
  297. defer resp.body.Close()
  298. nextAppendPosition, err = strconv.ParseInt(resp.headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
  299. if err != nil {
  300. return nextAppendPosition, initCRC, err
  301. }
  302. if bucket.getConfig().IsEnableCRC {
  303. err = checkCRC(resp, "AppendObject")
  304. if err != nil {
  305. return nextAppendPosition, resp.serverCRC, err
  306. }
  307. }
  308. return nextAppendPosition, resp.serverCRC, nil
  309. }
  310. //
  311. // DeleteObject 删除Object。
  312. //
  313. // objectKey 待删除Object。
  314. //
  315. // error 操作无错误为nil,非nil为错误信息。
  316. //
  317. func (bucket Bucket) DeleteObject(objectKey string) error {
  318. resp, err := bucket.do("DELETE", objectKey, "", "", nil, nil)
  319. if err != nil {
  320. return err
  321. }
  322. defer resp.body.Close()
  323. return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
  324. }
  325. //
  326. // DeleteObjects 批量删除object。
  327. //
  328. // objectKeys 待删除object类表。
  329. // options 删除选项,DeleteObjectsQuiet,是否是安静模式,默认不使用。
  330. //
  331. // DeleteObjectsResult 非安静模式的的返回值。
  332. // error 操作无错误为nil,非nil为错误信息。
  333. //
  334. func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
  335. out := DeleteObjectsResult{}
  336. dxml := deleteXML{}
  337. for _, key := range objectKeys {
  338. dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
  339. }
  340. isQuietStr, _ := findOption(options, deleteObjectsQuiet, "FALSE")
  341. isQuiet, _ := strconv.ParseBool(isQuietStr)
  342. dxml.Quiet = isQuiet
  343. encode := "&encoding-type=url"
  344. bs, err := xml.Marshal(dxml)
  345. if err != nil {
  346. return out, err
  347. }
  348. buffer := new(bytes.Buffer)
  349. buffer.Write(bs)
  350. contentType := http.DetectContentType(buffer.Bytes())
  351. options = append(options, ContentType(contentType))
  352. sum := md5.Sum(bs)
  353. b64 := base64.StdEncoding.EncodeToString(sum[:])
  354. options = append(options, ContentMD5(b64))
  355. resp, err := bucket.do("POST", "", "delete"+encode, "delete", options, buffer)
  356. if err != nil {
  357. return out, err
  358. }
  359. defer resp.body.Close()
  360. if !dxml.Quiet {
  361. if err = xmlUnmarshal(resp.body, &out); err == nil {
  362. err = decodeDeleteObjectsResult(&out)
  363. }
  364. }
  365. return out, err
  366. }
  367. //
  368. // IsObjectExist object是否存在。
  369. //
  370. // bool object是否存在,true存在,false不存在。error为nil时有效。
  371. //
  372. // error 操作无错误为nil,非nil为错误信息。
  373. //
  374. func (bucket Bucket) IsObjectExist(objectKey string) (bool, error) {
  375. listRes, err := bucket.ListObjects(Prefix(objectKey), MaxKeys(1))
  376. if err != nil {
  377. return false, err
  378. }
  379. if len(listRes.Objects) == 1 && listRes.Objects[0].Key == objectKey {
  380. return true, nil
  381. }
  382. return false, nil
  383. }
  384. //
  385. // ListObjects 获得Bucket下筛选后所有的object的列表。
  386. //
  387. // options ListObject的筛选行为。Prefix指定的前缀、MaxKeys最大数目、Marker第一个开始、Delimiter对Object名字进行分组的字符。
  388. //
  389. // 您有如下8个object,my-object-1, my-object-11, my-object-2, my-object-21,
  390. // my-object-22, my-object-3, my-object-31, my-object-32。如果您指定了Prefix为my-object-2,
  391. // 则返回my-object-2, my-object-21, my-object-22三个object。如果您指定了Marker为my-object-22,
  392. // 则返回my-object-3, my-object-31, my-object-32三个object。如果您指定MaxKeys则每次最多返回MaxKeys个,
  393. // 最后一次可能不足。这三个参数可以组合使用,实现分页等功能。如果把prefix设为某个文件夹名,就可以罗列以此prefix开头的文件,
  394. // 即该文件夹下递归的所有的文件和子文件夹。如果再把delimiter设置为"/"时,返回值就只罗列该文件夹下的文件,该文件夹下的子文件名
  395. // 返回在CommonPrefixes部分,子文件夹下递归的文件和文件夹不被显示。例如一个bucket存在三个object,fun/test.jpg、
  396. // fun/movie/001.avi、fun/movie/007.avi。若设定prefix为"fun/",则返回三个object;如果增加设定
  397. // delimiter为"/",则返回文件"fun/test.jpg"和前缀"fun/movie/",即实现了文件夹的逻辑。
  398. //
  399. // 常用场景,请参数示例sample/list_object.go。
  400. //
  401. // ListObjectsResponse 操作成功后的返回值,成员Objects为bucket中对象列表。error为nil时该返回值有效。
  402. //
  403. func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
  404. var out ListObjectsResult
  405. options = append(options, EncodingType("url"))
  406. params, err := handleParams(options)
  407. if err != nil {
  408. return out, err
  409. }
  410. resp, err := bucket.do("GET", "", params, "", nil, nil)
  411. if err != nil {
  412. return out, err
  413. }
  414. defer resp.body.Close()
  415. err = xmlUnmarshal(resp.body, &out)
  416. if err != nil {
  417. return out, err
  418. }
  419. err = decodeListObjectsResult(&out)
  420. return out, err
  421. }
  422. //
  423. // SetObjectMeta 设置Object的Meta。
  424. //
  425. // objectKey object
  426. // options 指定对象的属性,有以下可选项CacheControl、ContentDisposition、ContentEncoding、Expires、
  427. // ServerSideEncryption、Meta。
  428. //
  429. // error 操作无错误时error为nil,非nil为错误信息。
  430. //
  431. func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
  432. options = append(options, MetadataDirective(MetaReplace))
  433. _, err := bucket.CopyObject(objectKey, objectKey, options...)
  434. return err
  435. }
  436. //
  437. // GetObjectDetailedMeta 查询Object的头信息。
  438. //
  439. // objectKey object名称。
  440. // objectPropertyConstraints 对象的属性限制项,满足时正常返回,不满足时返回错误。现在项有IfModifiedSince、IfUnmodifiedSince、
  441. // IfMatch、IfNoneMatch。具体含义请参看 https://help.aliyun.com/document_detail/oss/api-reference/object/HeadObject.html
  442. //
  443. // http.Header 对象的meta,error为nil时有效。
  444. // error 操作无错误为nil,非nil为错误信息。
  445. //
  446. func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
  447. resp, err := bucket.do("HEAD", objectKey, "", "", options, nil)
  448. if err != nil {
  449. return nil, err
  450. }
  451. defer resp.body.Close()
  452. return resp.headers, nil
  453. }
  454. //
  455. // GetObjectMeta 查询Object的头信息。
  456. //
  457. // GetObjectMeta相比GetObjectDetailedMeta更轻量,仅返回指定Object的少量基本meta信息,
  458. // 包括该Object的ETag、Size(对象大小)、LastModified,其中Size由响应头Content-Length的数值表示。
  459. //
  460. // objectKey object名称。
  461. //
  462. // http.Header 对象的meta,error为nil时有效。
  463. // error 操作无错误为nil,非nil为错误信息。
  464. //
  465. func (bucket Bucket) GetObjectMeta(objectKey string) (http.Header, error) {
  466. resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil)
  467. if err != nil {
  468. return nil, err
  469. }
  470. defer resp.body.Close()
  471. return resp.headers, nil
  472. }
  473. //
  474. // SetObjectACL 修改Object的ACL权限。
  475. //
  476. // 只有Bucket Owner才有权限调用PutObjectACL来修改Object的ACL。Object ACL优先级高于Bucket ACL。
  477. // 例如Bucket ACL是private的,而Object ACL是public-read-write的,则访问这个Object时,
  478. // 先判断Object的ACL,所以所有用户都拥有这个Object的访问权限,即使这个Bucket是private bucket。
  479. // 如果某个Object从来没设置过ACL,则访问权限遵循Bucket ACL。
  480. //
  481. // Object的读操作包括GetObject,HeadObject,CopyObject和UploadPartCopy中的对source object的读;
  482. // Object的写操作包括:PutObject,PostObject,AppendObject,DeleteObject,
  483. // DeleteMultipleObjects,CompleteMultipartUpload以及CopyObject对新的Object的写。
  484. //
  485. // objectKey 设置权限的object。
  486. // objectAcl 对象权限。可选值PrivateACL(私有读写)、PublicReadACL(公共读私有写)、PublicReadWriteACL(公共读写)。
  487. //
  488. // error 操作无错误为nil,非nil为错误信息。
  489. //
  490. func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
  491. options := []Option{ObjectACL(objectACL)}
  492. resp, err := bucket.do("PUT", objectKey, "acl", "acl", options, nil)
  493. if err != nil {
  494. return err
  495. }
  496. defer resp.body.Close()
  497. return checkRespCode(resp.statusCode, []int{http.StatusOK})
  498. }
  499. //
  500. // GetObjectACL 获取对象的ACL权限。
  501. //
  502. // objectKey 获取权限的object。
  503. //
  504. // GetObjectAclResponse 获取权限操作返回值,error为nil时有效。GetObjectAclResponse.Acl为对象的权限。
  505. // error 操作无错误为nil,非nil为错误信息。
  506. //
  507. func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error) {
  508. var out GetObjectACLResult
  509. resp, err := bucket.do("GET", objectKey, "acl", "acl", nil, nil)
  510. if err != nil {
  511. return out, err
  512. }
  513. defer resp.body.Close()
  514. err = xmlUnmarshal(resp.body, &out)
  515. return out, err
  516. }
  517. // Private
  518. func (bucket Bucket) do(method, objectName, urlParams, subResource string,
  519. options []Option, data io.Reader) (*Response, error) {
  520. headers := make(map[string]string)
  521. err := handleOptions(headers, options)
  522. if err != nil {
  523. return nil, err
  524. }
  525. return bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
  526. urlParams, subResource, headers, data, 0)
  527. }
  528. func (bucket Bucket) getConfig() *Config {
  529. return bucket.Client.Config
  530. }
  531. func addContentType(options []Option, keys ...string) []Option {
  532. typ := TypeByExtension("")
  533. for _, key := range keys {
  534. typ = TypeByExtension(key)
  535. if typ != "" {
  536. break
  537. }
  538. }
  539. if typ == "" {
  540. typ = "application/octet-stream"
  541. }
  542. opts := []Option{ContentType(typ)}
  543. opts = append(opts, options...)
  544. return opts
  545. }