bucket.go 19 KB

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