bucket.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. package oss
  2. import (
  3. "bytes"
  4. "crypto/md5"
  5. "encoding/base64"
  6. "encoding/xml"
  7. "fmt"
  8. "hash"
  9. "hash/crc64"
  10. "io"
  11. "io/ioutil"
  12. "net/http"
  13. "net/url"
  14. "os"
  15. "strconv"
  16. "time"
  17. )
  18. // Bucket implements the operations of object.
  19. type Bucket struct {
  20. Client Client
  21. BucketName string
  22. }
  23. //
  24. // PutObject 新建Object,如果Object已存在,覆盖原有Object。
  25. //
  26. // objectKey 上传对象的名称,使用UTF-8编码、长度必须在1-1023字节之间、不能以“/”或者“\”字符开头。
  27. // reader io.Reader读取object的数据。
  28. // options 上传对象时可以指定对象的属性,可用选项有CacheControl、ContentDisposition、ContentEncoding、
  29. // Expires、ServerSideEncryption、ObjectACL、Meta,具体含义请参看
  30. // https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
  31. //
  32. // error 操作无错误为nil,非nil为错误信息。
  33. //
  34. func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
  35. opts := addContentType(options, objectKey)
  36. request := &PutObjectRequest{
  37. ObjectKey: objectKey,
  38. Reader: reader,
  39. }
  40. resp, err := bucket.DoPutObject(request, opts)
  41. if err != nil {
  42. return err
  43. }
  44. defer resp.Body.Close()
  45. return err
  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. request := &PutObjectRequest{
  64. ObjectKey: objectKey,
  65. Reader: fd,
  66. }
  67. resp, err := bucket.DoPutObject(request, opts)
  68. if err != nil {
  69. return err
  70. }
  71. defer resp.Body.Close()
  72. return err
  73. }
  74. //
  75. // DoPutObject 上传文件。
  76. //
  77. // request 上传请求。
  78. // options 上传选项。
  79. //
  80. // Response 上传请求返回值。
  81. // error 操作无错误为nil,非nil为错误信息。
  82. //
  83. func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
  84. isOptSet, _, _ := isOptionSet(options, HTTPHeaderContentType)
  85. if !isOptSet {
  86. options = addContentType(options, request.ObjectKey)
  87. }
  88. listener := getProgressListener(options)
  89. params := map[string]interface{}{}
  90. resp, err := bucket.do("PUT", request.ObjectKey, params, options, request.Reader, listener)
  91. if err != nil {
  92. return nil, err
  93. }
  94. if bucket.getConfig().IsEnableCRC {
  95. err = checkCRC(resp, "DoPutObject")
  96. if err != nil {
  97. return resp, err
  98. }
  99. }
  100. err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
  101. return resp, err
  102. }
  103. //
  104. // GetObject 下载文件。
  105. //
  106. // objectKey 下载的文件名称。
  107. // options 对象的属性限制项,可选值有Range、IfModifiedSince、IfUnmodifiedSince、IfMatch、
  108. // IfNoneMatch、AcceptEncoding,详细请参考
  109. // https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
  110. //
  111. // io.ReadCloser reader,读取数据后需要close。error为nil时有效。
  112. // error 操作无错误为nil,非nil为错误信息。
  113. //
  114. func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
  115. result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
  116. if err != nil {
  117. return nil, err
  118. }
  119. return result.Response.Body, nil
  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. tempFilePath := filePath + TempFileSuffix
  132. // 读取Object内容
  133. result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
  134. if err != nil {
  135. return err
  136. }
  137. defer result.Response.Body.Close()
  138. // 如果文件不存在则创建,存在则清空
  139. fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
  140. if err != nil {
  141. return err
  142. }
  143. // 存储数据到文件
  144. _, err = io.Copy(fd, result.Response.Body)
  145. fd.Close()
  146. if err != nil {
  147. return err
  148. }
  149. // 比较CRC值
  150. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  151. if bucket.getConfig().IsEnableCRC && !hasRange {
  152. result.Response.ClientCRC = result.ClientCRC.Sum64()
  153. err = checkCRC(result.Response, "GetObjectToFile")
  154. if err != nil {
  155. os.Remove(tempFilePath)
  156. return err
  157. }
  158. }
  159. return os.Rename(tempFilePath, filePath)
  160. }
  161. //
  162. // DoGetObject 下载文件
  163. //
  164. // request 下载请求
  165. // options 对象的属性限制项。详见GetObject的options。
  166. //
  167. // GetObjectResult 下载请求返回值。
  168. // error 操作无错误为nil,非nil为错误信息。
  169. //
  170. func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
  171. params := map[string]interface{}{}
  172. resp, err := bucket.do("GET", request.ObjectKey, params, options, nil, nil)
  173. if err != nil {
  174. return nil, err
  175. }
  176. result := &GetObjectResult{
  177. Response: resp,
  178. }
  179. // crc
  180. var crcCalc hash.Hash64
  181. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  182. if bucket.getConfig().IsEnableCRC && !hasRange {
  183. crcCalc = crc64.New(crcTable())
  184. result.ServerCRC = resp.ServerCRC
  185. result.ClientCRC = crcCalc
  186. }
  187. // progress
  188. listener := getProgressListener(options)
  189. contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
  190. resp.Body = ioutil.NopCloser(TeeReader(resp.Body, crcCalc, contentLen, listener, nil))
  191. return result, nil
  192. }
  193. //
  194. // CopyObject 同一个bucket内拷贝Object。
  195. //
  196. // srcObjectKey Copy的源对象。
  197. // destObjectKey Copy的目标对象。
  198. // options Copy对象时,您可以指定源对象的限制条件,满足限制条件时copy,不满足时返回错误,您可以选择如下选项CopySourceIfMatch、
  199. // CopySourceIfNoneMatch、CopySourceIfModifiedSince、CopySourceIfUnmodifiedSince、MetadataDirective。
  200. // Copy对象时,您可以指定目标对象的属性,如CacheControl、ContentDisposition、ContentEncoding、Expires、
  201. // ServerSideEncryption、ObjectACL、Meta,选项的含义请参看
  202. // https://help.aliyun.com/document_detail/oss/api-reference/object/CopyObject.html
  203. //
  204. // error 操作无错误为nil,非nil为错误信息。
  205. //
  206. func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  207. var out CopyObjectResult
  208. options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
  209. params := map[string]interface{}{}
  210. resp, err := bucket.do("PUT", destObjectKey, params, options, nil, nil)
  211. if err != nil {
  212. return out, err
  213. }
  214. defer resp.Body.Close()
  215. err = xmlUnmarshal(resp.Body, &out)
  216. return out, err
  217. }
  218. //
  219. // CopyObjectTo bucket间拷贝object。
  220. //
  221. // srcObjectKey 源Object名称。源Bucket名称为Bucket.BucketName。
  222. // destBucketName 目标Bucket名称。
  223. // destObjectKey 目标Object名称。
  224. // options Copy选项,详见CopyObject的options。
  225. //
  226. // error 操作无错误为nil,非nil为错误信息。
  227. //
  228. func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
  229. return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
  230. }
  231. //
  232. // CopyObjectFrom bucket间拷贝object。
  233. //
  234. // srcBucketName 源Bucket名称。
  235. // srcObjectKey 源Object名称。
  236. // destObjectKey 目标Object名称。目标Bucket名称为Bucket.BucketName。
  237. // options Copy选项,详见CopyObject的options。
  238. //
  239. // error 操作无错误为nil,非nil为错误信息。
  240. //
  241. func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  242. destBucketName := bucket.BucketName
  243. var out CopyObjectResult
  244. srcBucket, err := bucket.Client.Bucket(srcBucketName)
  245. if err != nil {
  246. return out, err
  247. }
  248. return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
  249. }
  250. func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  251. var out CopyObjectResult
  252. options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
  253. headers := make(map[string]string)
  254. err := handleOptions(headers, options)
  255. if err != nil {
  256. return out, err
  257. }
  258. params := map[string]interface{}{}
  259. resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)
  260. if err != nil {
  261. return out, err
  262. }
  263. defer resp.Body.Close()
  264. err = xmlUnmarshal(resp.Body, &out)
  265. return out, err
  266. }
  267. //
  268. // AppendObject 追加方式上传。
  269. //
  270. // AppendObject参数必须包含position,其值指定从何处进行追加。首次追加操作的position必须为0,
  271. // 后续追加操作的position是Object的当前长度。例如,第一次Append Object请求指定position值为0,
  272. // content-length是65536;那么,第二次Append Object需要指定position为65536。
  273. // 每次操作成功后,响应头部x-oss-next-append-position也会标明下一次追加的position。
  274. //
  275. // objectKey 需要追加的Object。
  276. // reader io.Reader,读取追的内容。
  277. // appendPosition object追加的起始位置。
  278. // destObjectProperties 第一次追加时指定新对象的属性,如CacheControl、ContentDisposition、ContentEncoding、
  279. // Expires、ServerSideEncryption、ObjectACL。
  280. //
  281. // int64 下次追加的开始位置,error为nil空时有效。
  282. // error 操作无错误为nil,非nil为错误信息。
  283. //
  284. func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
  285. request := &AppendObjectRequest{
  286. ObjectKey: objectKey,
  287. Reader: reader,
  288. Position: appendPosition,
  289. }
  290. result, err := bucket.DoAppendObject(request, options)
  291. if err != nil {
  292. return appendPosition, err
  293. }
  294. return result.NextPosition, err
  295. }
  296. //
  297. // DoAppendObject 追加上传。
  298. //
  299. // request 追加上传请求。
  300. // options 追加上传选项。
  301. //
  302. // AppendObjectResult 追加上传请求返回值。
  303. // error 操作无错误为nil,非nil为错误信息。
  304. //
  305. func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Option) (*AppendObjectResult, error) {
  306. params := map[string]interface{}{}
  307. params["append"] = nil
  308. params["position"] = strconv.FormatInt(request.Position, 10)
  309. headers := make(map[string]string)
  310. opts := addContentType(options, request.ObjectKey)
  311. handleOptions(headers, opts)
  312. var initCRC uint64
  313. isCRCSet, initCRCOpt, _ := isOptionSet(options, initCRC64)
  314. if isCRCSet {
  315. initCRC = initCRCOpt.(uint64)
  316. }
  317. listener := getProgressListener(options)
  318. handleOptions(headers, opts)
  319. resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, headers,
  320. request.Reader, initCRC, listener)
  321. if err != nil {
  322. return nil, err
  323. }
  324. defer resp.Body.Close()
  325. nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
  326. result := &AppendObjectResult{
  327. NextPosition: nextPosition,
  328. CRC: resp.ServerCRC,
  329. }
  330. if bucket.getConfig().IsEnableCRC && isCRCSet {
  331. err = checkCRC(resp, "AppendObject")
  332. if err != nil {
  333. return result, err
  334. }
  335. }
  336. return result, nil
  337. }
  338. //
  339. // DeleteObject 删除Object。
  340. //
  341. // objectKey 待删除Object。
  342. //
  343. // error 操作无错误为nil,非nil为错误信息。
  344. //
  345. func (bucket Bucket) DeleteObject(objectKey string) error {
  346. params := map[string]interface{}{}
  347. resp, err := bucket.do("DELETE", objectKey, params, nil, nil, nil)
  348. if err != nil {
  349. return err
  350. }
  351. defer resp.Body.Close()
  352. return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
  353. }
  354. //
  355. // DeleteObjects 批量删除object。
  356. //
  357. // objectKeys 待删除object类表。
  358. // options 删除选项,DeleteObjectsQuiet,是否是安静模式,默认不使用。
  359. //
  360. // DeleteObjectsResult 非安静模式的的返回值。
  361. // error 操作无错误为nil,非nil为错误信息。
  362. //
  363. func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
  364. out := DeleteObjectsResult{}
  365. dxml := deleteXML{}
  366. for _, key := range objectKeys {
  367. dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
  368. }
  369. isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
  370. dxml.Quiet = isQuiet.(bool)
  371. bs, err := xml.Marshal(dxml)
  372. if err != nil {
  373. return out, err
  374. }
  375. buffer := new(bytes.Buffer)
  376. buffer.Write(bs)
  377. contentType := http.DetectContentType(buffer.Bytes())
  378. options = append(options, ContentType(contentType))
  379. sum := md5.Sum(bs)
  380. b64 := base64.StdEncoding.EncodeToString(sum[:])
  381. options = append(options, ContentMD5(b64))
  382. params := map[string]interface{}{}
  383. params["delete"] = nil
  384. params["encoding-type"] = "url"
  385. resp, err := bucket.do("POST", "", params, options, buffer, nil)
  386. if err != nil {
  387. return out, err
  388. }
  389. defer resp.Body.Close()
  390. if !dxml.Quiet {
  391. if err = xmlUnmarshal(resp.Body, &out); err == nil {
  392. err = decodeDeleteObjectsResult(&out)
  393. }
  394. }
  395. return out, err
  396. }
  397. //
  398. // IsObjectExist object是否存在。
  399. //
  400. // bool object是否存在,true存在,false不存在。error为nil时有效。
  401. //
  402. // error 操作无错误为nil,非nil为错误信息。
  403. //
  404. func (bucket Bucket) IsObjectExist(objectKey string) (bool, error) {
  405. _, err := bucket.GetObjectMeta(objectKey)
  406. if err == nil {
  407. return true, nil
  408. }
  409. switch err.(type) {
  410. case ServiceError:
  411. if err.(ServiceError).StatusCode == 404 && err.(ServiceError).Code == "NoSuchKey" {
  412. return false, nil
  413. }
  414. }
  415. return false, err
  416. }
  417. //
  418. // ListObjects 获得Bucket下筛选后所有的object的列表。
  419. //
  420. // options ListObject的筛选行为。Prefix指定的前缀、MaxKeys最大数目、Marker第一个开始、Delimiter对Object名字进行分组的字符。
  421. //
  422. // 您有如下8个object,my-object-1, my-object-11, my-object-2, my-object-21,
  423. // my-object-22, my-object-3, my-object-31, my-object-32。如果您指定了Prefix为my-object-2,
  424. // 则返回my-object-2, my-object-21, my-object-22三个object。如果您指定了Marker为my-object-22,
  425. // 则返回my-object-3, my-object-31, my-object-32三个object。如果您指定MaxKeys则每次最多返回MaxKeys个,
  426. // 最后一次可能不足。这三个参数可以组合使用,实现分页等功能。如果把prefix设为某个文件夹名,就可以罗列以此prefix开头的文件,
  427. // 即该文件夹下递归的所有的文件和子文件夹。如果再把delimiter设置为"/"时,返回值就只罗列该文件夹下的文件,该文件夹下的子文件名
  428. // 返回在CommonPrefixes部分,子文件夹下递归的文件和文件夹不被显示。例如一个bucket存在三个object,fun/test.jpg、
  429. // fun/movie/001.avi、fun/movie/007.avi。若设定prefix为"fun/",则返回三个object;如果增加设定
  430. // delimiter为"/",则返回文件"fun/test.jpg"和前缀"fun/movie/",即实现了文件夹的逻辑。
  431. //
  432. // 常用场景,请参数示例sample/list_object.go。
  433. //
  434. // ListObjectsResponse 操作成功后的返回值,成员Objects为bucket中对象列表。error为nil时该返回值有效。
  435. //
  436. func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
  437. var out ListObjectsResult
  438. options = append(options, EncodingType("url"))
  439. params, err := getRawParams(options)
  440. if err != nil {
  441. return out, err
  442. }
  443. resp, err := bucket.do("GET", "", params, nil, nil, nil)
  444. if err != nil {
  445. return out, err
  446. }
  447. defer resp.Body.Close()
  448. err = xmlUnmarshal(resp.Body, &out)
  449. if err != nil {
  450. return out, err
  451. }
  452. err = decodeListObjectsResult(&out)
  453. return out, err
  454. }
  455. //
  456. // SetObjectMeta 设置Object的Meta。
  457. //
  458. // objectKey object
  459. // options 指定对象的属性,有以下可选项CacheControl、ContentDisposition、ContentEncoding、Expires、
  460. // ServerSideEncryption、Meta。
  461. //
  462. // error 操作无错误时error为nil,非nil为错误信息。
  463. //
  464. func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
  465. options = append(options, MetadataDirective(MetaReplace))
  466. _, err := bucket.CopyObject(objectKey, objectKey, options...)
  467. return err
  468. }
  469. //
  470. // GetObjectDetailedMeta 查询Object的头信息。
  471. //
  472. // objectKey object名称。
  473. // objectPropertyConstraints 对象的属性限制项,满足时正常返回,不满足时返回错误。现在项有IfModifiedSince、IfUnmodifiedSince、
  474. // IfMatch、IfNoneMatch。具体含义请参看 https://help.aliyun.com/document_detail/oss/api-reference/object/HeadObject.html
  475. //
  476. // http.Header 对象的meta,error为nil时有效。
  477. // error 操作无错误为nil,非nil为错误信息。
  478. //
  479. func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
  480. params := map[string]interface{}{}
  481. resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
  482. if err != nil {
  483. return nil, err
  484. }
  485. defer resp.Body.Close()
  486. return resp.Headers, nil
  487. }
  488. //
  489. // GetObjectMeta 查询Object的头信息。
  490. //
  491. // GetObjectMeta相比GetObjectDetailedMeta更轻量,仅返回指定Object的少量基本meta信息,
  492. // 包括该Object的ETag、Size(对象大小)、LastModified,其中Size由响应头Content-Length的数值表示。
  493. //
  494. // objectKey object名称。
  495. //
  496. // http.Header 对象的meta,error为nil时有效。
  497. // error 操作无错误为nil,非nil为错误信息。
  498. //
  499. func (bucket Bucket) GetObjectMeta(objectKey string) (http.Header, error) {
  500. params := map[string]interface{}{}
  501. params["objectMeta"] = nil
  502. //resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil, nil)
  503. resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
  504. if err != nil {
  505. return nil, err
  506. }
  507. defer resp.Body.Close()
  508. return resp.Headers, nil
  509. }
  510. //
  511. // SetObjectACL 修改Object的ACL权限。
  512. //
  513. // 只有Bucket Owner才有权限调用PutObjectACL来修改Object的ACL。Object ACL优先级高于Bucket ACL。
  514. // 例如Bucket ACL是private的,而Object ACL是public-read-write的,则访问这个Object时,
  515. // 先判断Object的ACL,所以所有用户都拥有这个Object的访问权限,即使这个Bucket是private bucket。
  516. // 如果某个Object从来没设置过ACL,则访问权限遵循Bucket ACL。
  517. //
  518. // Object的读操作包括GetObject,HeadObject,CopyObject和UploadPartCopy中的对source object的读;
  519. // Object的写操作包括:PutObject,PostObject,AppendObject,DeleteObject,
  520. // DeleteMultipleObjects,CompleteMultipartUpload以及CopyObject对新的Object的写。
  521. //
  522. // objectKey 设置权限的object。
  523. // objectAcl 对象权限。可选值PrivateACL(私有读写)、PublicReadACL(公共读私有写)、PublicReadWriteACL(公共读写)。
  524. //
  525. // error 操作无错误为nil,非nil为错误信息。
  526. //
  527. func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
  528. options := []Option{ObjectACL(objectACL)}
  529. params := map[string]interface{}{}
  530. params["acl"] = nil
  531. resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
  532. if err != nil {
  533. return err
  534. }
  535. defer resp.Body.Close()
  536. return checkRespCode(resp.StatusCode, []int{http.StatusOK})
  537. }
  538. //
  539. // GetObjectACL 获取对象的ACL权限。
  540. //
  541. // objectKey 获取权限的object。
  542. //
  543. // GetObjectAclResponse 获取权限操作返回值,error为nil时有效。GetObjectAclResponse.Acl为对象的权限。
  544. // error 操作无错误为nil,非nil为错误信息。
  545. //
  546. func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error) {
  547. var out GetObjectACLResult
  548. params := map[string]interface{}{}
  549. params["acl"] = nil
  550. resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
  551. if err != nil {
  552. return out, err
  553. }
  554. defer resp.Body.Close()
  555. err = xmlUnmarshal(resp.Body, &out)
  556. return out, err
  557. }
  558. //
  559. // PutSymlink 创建符号链接。
  560. //
  561. // 符号链接的目标文件类型不能为符号链接。
  562. // 创建符号链接时: 不检查目标文件是否存在, 不检查目标文件类型是否合法, 不检查目标文件是否有权限访问。
  563. // 以上检查,都推迟到GetObject等需要访问目标文件的API。
  564. // 如果试图添加的文件已经存在,并且有访问权限。新添加的文件将覆盖原来的文件。
  565. // 如果在PutSymlink的时候,携带以x-oss-meta-为前缀的参数,则视为user meta。
  566. //
  567. // symObjectKey 要创建的符号链接文件。
  568. // targetObjectKey 目标文件。
  569. //
  570. // error 操作无错误为nil,非nil为错误信息。
  571. //
  572. func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
  573. options = append(options, symlinkTarget(url.QueryEscape(targetObjectKey)))
  574. params := map[string]interface{}{}
  575. params["symlink"] = nil
  576. resp, err := bucket.do("PUT", symObjectKey, params, options, nil, nil)
  577. if err != nil {
  578. return err
  579. }
  580. defer resp.Body.Close()
  581. return checkRespCode(resp.StatusCode, []int{http.StatusOK})
  582. }
  583. //
  584. // GetSymlink 获取符号链接的目标文件。
  585. // 如果符号链接不存在返回404。
  586. //
  587. // objectKey 获取目标文件的符号链接object。
  588. //
  589. // error 操作无错误为nil,非nil为错误信息。当error为nil时,返回的string为目标文件,否则该值无效。
  590. //
  591. func (bucket Bucket) GetSymlink(objectKey string) (http.Header, error) {
  592. params := map[string]interface{}{}
  593. params["symlink"] = nil
  594. resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
  595. if err != nil {
  596. return nil, err
  597. }
  598. defer resp.Body.Close()
  599. targetObjectKey := resp.Headers.Get(HTTPHeaderOssSymlinkTarget)
  600. targetObjectKey, err = url.QueryUnescape(targetObjectKey)
  601. if err != nil {
  602. return resp.Headers, err
  603. }
  604. resp.Headers.Set(HTTPHeaderOssSymlinkTarget, targetObjectKey)
  605. return resp.Headers, err
  606. }
  607. //
  608. // RestoreObject 恢复处于冷冻状态的归档类型Object进入读就绪状态。
  609. //
  610. // 一个Archive类型的object初始时处于冷冻状态。
  611. //
  612. // 针对处于冷冻状态的object调用restore命令,返回成功。object处于解冻中,服务端执行解冻,在此期间再次调用restore命令,同样成功,且不会延长object可读状态持续时间。
  613. // 待服务端执行完成解冻任务后,object就进入了解冻状态,此时用户可以读取object。
  614. // 解冻状态默认持续1天,对于解冻状态的object调用restore命令,会将object的解冻状态延长一天,最多可以延长到7天,之后object又回到初始时的冷冻状态。
  615. //
  616. // objectKey 需要恢复状态的object名称。
  617. //
  618. // error 操作无错误为nil,非nil为错误信息。
  619. //
  620. func (bucket Bucket) RestoreObject(objectKey string) error {
  621. params := map[string]interface{}{}
  622. params["restore"] = nil
  623. resp, err := bucket.do("POST", objectKey, params, nil, nil, nil)
  624. if err != nil {
  625. return err
  626. }
  627. defer resp.Body.Close()
  628. return checkRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
  629. }
  630. //
  631. // SignURL 获取签名URL。
  632. //
  633. // objectKey 获取URL的object。
  634. // signURLConfig 获取URL的配置。
  635. //
  636. // 返回URL字符串,error为nil时有效。
  637. // error 操作无错误为nil,非nil为错误信息。
  638. //
  639. func (bucket Bucket) SignURL(objectKey string, method HTTPMethod, expiredInSec int64, options ...Option) (string, error) {
  640. if expiredInSec < 0 {
  641. return "", fmt.Errorf("invalid expires: %d, expires must bigger than 0", expiredInSec)
  642. }
  643. expiration := time.Now().Unix() + expiredInSec
  644. params, err := getRawParams(options)
  645. if err != nil {
  646. return "", err
  647. }
  648. headers := make(map[string]string)
  649. err = handleOptions(headers, options)
  650. if err != nil {
  651. return "", err
  652. }
  653. return bucket.Client.Conn.signURL(method, bucket.BucketName, objectKey, expiration, params, headers), nil
  654. }
  655. //
  656. // PutObjectWithURL 新建Object,如果Object已存在,覆盖原有Object。
  657. // PutObjectWithURL 不会根据key生成minetype。
  658. //
  659. // signedURL 签名的URL。
  660. // reader io.Reader读取object的数据。
  661. // options 上传对象时可以指定对象的属性,可用选项有CacheControl、ContentDisposition、ContentEncoding、
  662. // Expires、ServerSideEncryption、ObjectACL、Meta,具体含义请参看
  663. // https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
  664. //
  665. // error 操作无错误为nil,非nil为错误信息。
  666. //
  667. func (bucket Bucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...Option) error {
  668. resp, err := bucket.DoPutObjectWithURL(signedURL, reader, options)
  669. if err != nil {
  670. return err
  671. }
  672. defer resp.Body.Close()
  673. return err
  674. }
  675. //
  676. // PutObjectFromFileWithURL 新建Object,内容从本地文件中读取。
  677. // PutObjectFromFileWithURL 不会根据key、filePath生成minetype。
  678. //
  679. // signedURL 签名的URL。
  680. // filePath 本地文件,如 dir/file.txt,上传对象的值为该文件内容。
  681. // options 上传对象时可以指定对象的属性。详见PutObject的options。
  682. //
  683. // error 操作无错误为nil,非nil为错误信息。
  684. //
  685. func (bucket Bucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...Option) error {
  686. fd, err := os.Open(filePath)
  687. if err != nil {
  688. return err
  689. }
  690. defer fd.Close()
  691. resp, err := bucket.DoPutObjectWithURL(signedURL, fd, options)
  692. if err != nil {
  693. return err
  694. }
  695. defer resp.Body.Close()
  696. return err
  697. }
  698. //
  699. // DoPutObjectWithURL 上传文件。
  700. //
  701. // signedURL 签名的URL。
  702. // reader io.Reader读取object的数据。
  703. // options 上传选项。
  704. //
  705. // Response 上传请求返回值。
  706. // error 操作无错误为nil,非nil为错误信息。
  707. //
  708. func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []Option) (*Response, error) {
  709. listener := getProgressListener(options)
  710. params := map[string]interface{}{}
  711. resp, err := bucket.doURL("PUT", signedURL, params, options, reader, listener)
  712. if err != nil {
  713. return nil, err
  714. }
  715. if bucket.getConfig().IsEnableCRC {
  716. err = checkCRC(resp, "DoPutObjectWithURL")
  717. if err != nil {
  718. return resp, err
  719. }
  720. }
  721. err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
  722. return resp, err
  723. }
  724. //
  725. // GetObjectWithURL 下载文件。
  726. //
  727. // signedURL 签名的URL。
  728. // options 对象的属性限制项,可选值有Range、IfModifiedSince、IfUnmodifiedSince、IfMatch、
  729. // IfNoneMatch、AcceptEncoding,详细请参考
  730. // https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
  731. //
  732. // io.ReadCloser reader,读取数据后需要close。error为nil时有效。
  733. // error 操作无错误为nil,非nil为错误信息。
  734. //
  735. func (bucket Bucket) GetObjectWithURL(signedURL string, options ...Option) (io.ReadCloser, error) {
  736. result, err := bucket.DoGetObjectWithURL(signedURL, options)
  737. if err != nil {
  738. return nil, err
  739. }
  740. return result.Response.Body, nil
  741. }
  742. //
  743. // GetObjectToFile 下载文件。
  744. //
  745. // signedURL 签名的URL。
  746. // filePath 下载对象的内容写到该本地文件。
  747. // options 对象的属性限制项。详见GetObject的options。
  748. //
  749. // error 操作无错误时返回error为nil,非nil为错误说明。
  750. //
  751. func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options ...Option) error {
  752. tempFilePath := filePath + TempFileSuffix
  753. // 读取Object内容
  754. result, err := bucket.DoGetObjectWithURL(signedURL, options)
  755. if err != nil {
  756. return err
  757. }
  758. defer result.Response.Body.Close()
  759. // 如果文件不存在则创建,存在则清空
  760. fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
  761. if err != nil {
  762. return err
  763. }
  764. // 存储数据到文件
  765. _, err = io.Copy(fd, result.Response.Body)
  766. fd.Close()
  767. if err != nil {
  768. return err
  769. }
  770. // 比较CRC值
  771. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  772. if bucket.getConfig().IsEnableCRC && !hasRange {
  773. result.Response.ClientCRC = result.ClientCRC.Sum64()
  774. err = checkCRC(result.Response, "GetObjectToFileWithURL")
  775. if err != nil {
  776. os.Remove(tempFilePath)
  777. return err
  778. }
  779. }
  780. return os.Rename(tempFilePath, filePath)
  781. }
  782. //
  783. // DoGetObjectWithURL 下载文件
  784. //
  785. // signedURL 签名的URL。
  786. // options 对象的属性限制项。详见GetObject的options。
  787. //
  788. // GetObjectResult 下载请求返回值。
  789. // error 操作无错误为nil,非nil为错误信息。
  790. //
  791. func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*GetObjectResult, error) {
  792. params := map[string]interface{}{}
  793. resp, err := bucket.doURL("GET", signedURL, params, options, nil, nil)
  794. if err != nil {
  795. return nil, err
  796. }
  797. result := &GetObjectResult{
  798. Response: resp,
  799. }
  800. // crc
  801. var crcCalc hash.Hash64
  802. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  803. if bucket.getConfig().IsEnableCRC && !hasRange {
  804. crcCalc = crc64.New(crcTable())
  805. result.ServerCRC = resp.ServerCRC
  806. result.ClientCRC = crcCalc
  807. }
  808. // progress
  809. listener := getProgressListener(options)
  810. contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
  811. resp.Body = ioutil.NopCloser(TeeReader(resp.Body, crcCalc, contentLen, listener, nil))
  812. return result, nil
  813. }
  814. // Private
  815. func (bucket Bucket) do(method, objectName string, params map[string]interface{}, options []Option,
  816. data io.Reader, listener ProgressListener) (*Response, error) {
  817. headers := make(map[string]string)
  818. err := handleOptions(headers, options)
  819. if err != nil {
  820. return nil, err
  821. }
  822. return bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
  823. params, headers, data, 0, listener)
  824. }
  825. func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[string]interface{}, options []Option,
  826. data io.Reader, listener ProgressListener) (*Response, error) {
  827. headers := make(map[string]string)
  828. err := handleOptions(headers, options)
  829. if err != nil {
  830. return nil, err
  831. }
  832. return bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)
  833. }
  834. func (bucket Bucket) getConfig() *Config {
  835. return bucket.Client.Config
  836. }
  837. func addContentType(options []Option, keys ...string) []Option {
  838. typ := TypeByExtension("")
  839. for _, key := range keys {
  840. typ = TypeByExtension(key)
  841. if typ != "" {
  842. break
  843. }
  844. }
  845. if typ == "" {
  846. typ = "application/octet-stream"
  847. }
  848. opts := []Option{ContentType(typ)}
  849. opts = append(opts, options...)
  850. return opts
  851. }