bucket.go 16 KB

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