bucket.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  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. // PutObject creates a new object and it will overwrite the original one if it exists already.
  24. //
  25. // objectKey the object key in UTF-8 encoding. The length must be between 1 and 1023, and cannot start with "/" or "\".
  26. // reader io.Reader instance for reading the data for uploading
  27. // options the options for uploading the object. The valid options here are CacheControl, ContentDisposition, ContentEncoding
  28. // Expires, ServerSideEncryption, ObjectACL and Meta. Refer to the link below for more details.
  29. // https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
  30. //
  31. // error it's nil if no error, otherwise it's an error object.
  32. //
  33. func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
  34. opts := addContentType(options, objectKey)
  35. request := &PutObjectRequest{
  36. ObjectKey: objectKey,
  37. Reader: reader,
  38. }
  39. resp, err := bucket.DoPutObject(request, opts)
  40. if err != nil {
  41. return err
  42. }
  43. defer resp.Body.Close()
  44. return err
  45. }
  46. // PutObjectFromFile creates a new object from the local file.
  47. //
  48. // objectKey object key.
  49. // filePath the local file path to upload.
  50. // options the options for uploading the object. Refer to the parameter options in PutObject for more details.
  51. //
  52. // error it's nil if no error, otherwise it's an error object.
  53. //
  54. func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Option) error {
  55. fd, err := os.Open(filePath)
  56. if err != nil {
  57. return err
  58. }
  59. defer fd.Close()
  60. opts := addContentType(options, filePath, objectKey)
  61. request := &PutObjectRequest{
  62. ObjectKey: objectKey,
  63. Reader: fd,
  64. }
  65. resp, err := bucket.DoPutObject(request, opts)
  66. if err != nil {
  67. return err
  68. }
  69. defer resp.Body.Close()
  70. return err
  71. }
  72. // DoPutObject does the actual upload work.
  73. //
  74. // request the request instance for uploading an object.
  75. // options the options for uploading an object.
  76. //
  77. // Response the response from OSS.
  78. // error it's nil if no error, otherwise it's an error object.
  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. listener := getProgressListener(options)
  86. params := map[string]interface{}{}
  87. resp, err := bucket.do("PUT", request.ObjectKey, params, options, request.Reader, listener)
  88. if err != nil {
  89. return nil, err
  90. }
  91. if bucket.getConfig().IsEnableCRC {
  92. err = checkCRC(resp, "DoPutObject")
  93. if err != nil {
  94. return resp, err
  95. }
  96. }
  97. err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
  98. return resp, err
  99. }
  100. // GetObject downloads the object.
  101. //
  102. // objectKey the object key.
  103. // options the options for downloading the object. The valid values are: Range, IfModifiedSince, IfUnmodifiedSince, IfMatch,
  104. // IfNoneMatch, AcceptEncoding. For more details, please check out:
  105. // https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
  106. //
  107. // io.ReadCloser reader instance for reading data from response. It must be called close() after the usage and only valid when error is nil.
  108. // error it's nil if no error, otherwise it's an error object.
  109. //
  110. func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
  111. result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
  112. if err != nil {
  113. return nil, err
  114. }
  115. return result.Response.Body, nil
  116. }
  117. // GetObjectToFile downloads the data to a local file.
  118. //
  119. // objectKey the object key to download.
  120. // filePath the local file to store the object data.
  121. // options the options for downloading the object. Refer to the parameter options in method GetObject for more details.
  122. //
  123. // error it's nil if no error, otherwise it's an error object.
  124. //
  125. func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
  126. tempFilePath := filePath + TempFileSuffix
  127. // Calls the API to actually download the object. Returns the result instance.
  128. result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
  129. if err != nil {
  130. return err
  131. }
  132. defer result.Response.Body.Close()
  133. // If the local file does not exist, create a new one. If it exists, overwrite it.
  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. // Copy the data to the local file path.
  139. _, err = io.Copy(fd, result.Response.Body)
  140. fd.Close()
  141. if err != nil {
  142. return err
  143. }
  144. // Compares the CRC value
  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. // DoGetObject is the actual API that gets the object. It's the internal function called by other public APIs.
  157. //
  158. // request the request to download the object.
  159. // options the options for downloading the file. Checks out the parameter options in method GetObject.
  160. //
  161. // GetObjectResult the result instance of getting the object.
  162. // error it's nil if no error, otherwise it's an error object.
  163. //
  164. func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
  165. params := map[string]interface{}{}
  166. resp, err := bucket.do("GET", request.ObjectKey, params, options, nil, nil)
  167. if err != nil {
  168. return nil, err
  169. }
  170. result := &GetObjectResult{
  171. Response: resp,
  172. }
  173. // CRC
  174. var crcCalc hash.Hash64
  175. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  176. if bucket.getConfig().IsEnableCRC && !hasRange {
  177. crcCalc = crc64.New(crcTable())
  178. result.ServerCRC = resp.ServerCRC
  179. result.ClientCRC = crcCalc
  180. }
  181. // Progress
  182. listener := getProgressListener(options)
  183. contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
  184. resp.Body = ioutil.NopCloser(TeeReader(resp.Body, crcCalc, contentLen, listener, nil))
  185. return result, nil
  186. }
  187. // CopyObject copies the object inside the bucket.
  188. //
  189. // srcObjectKey the source object to copy.
  190. // destObjectKey the target object to copy.
  191. // options options for copying an object. You can specify the conditions of copy. The valid conditions are CopySourceIfMatch,
  192. // CopySourceIfNoneMatch, CopySourceIfModifiedSince, CopySourceIfUnmodifiedSince, MetadataDirective.
  193. // Also you can specify the target object's attributes, such as CacheControl, ContentDisposition, ContentEncoding, Expires,
  194. // ServerSideEncryption, ObjectACL, Meta. Refer to the link below for more details :
  195. // https://help.aliyun.com/document_detail/oss/api-reference/object/CopyObject.html
  196. //
  197. // error it's nil if no error, otherwise it's an error object.
  198. //
  199. func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  200. var out CopyObjectResult
  201. options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
  202. params := map[string]interface{}{}
  203. resp, err := bucket.do("PUT", destObjectKey, params, options, nil, nil)
  204. if err != nil {
  205. return out, err
  206. }
  207. defer resp.Body.Close()
  208. err = xmlUnmarshal(resp.Body, &out)
  209. return out, err
  210. }
  211. // CopyObjectTo copies the object to another bucket.
  212. //
  213. // srcObjectKey source object key. The source bucket is Bucket.BucketName .
  214. // destBucketName target bucket name.
  215. // destObjectKey target object name.
  216. // options copy options, check out parameter options in function CopyObject for more details.
  217. //
  218. // error it's nil if no error, otherwise it's an error object.
  219. //
  220. func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
  221. return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
  222. }
  223. //
  224. // CopyObjectFrom copies the object to another bucket.
  225. //
  226. // srcBucketName source bucket name.
  227. // srcObjectKey source object name.
  228. // destObjectKey target object name. The target bucket name is Bucket.BucketName.
  229. // options copy options. Check out parameter options in function CopyObject.
  230. //
  231. // error it's nil if no error, otherwise it's an error object.
  232. //
  233. func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  234. destBucketName := bucket.BucketName
  235. var out CopyObjectResult
  236. srcBucket, err := bucket.Client.Bucket(srcBucketName)
  237. if err != nil {
  238. return out, err
  239. }
  240. return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
  241. }
  242. func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
  243. var out CopyObjectResult
  244. options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
  245. headers := make(map[string]string)
  246. err := handleOptions(headers, options)
  247. if err != nil {
  248. return out, err
  249. }
  250. params := map[string]interface{}{}
  251. resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)
  252. if err != nil {
  253. return out, err
  254. }
  255. defer resp.Body.Close()
  256. err = xmlUnmarshal(resp.Body, &out)
  257. return out, err
  258. }
  259. // AppendObject uploads the data in the way of appending an existing or new object.
  260. //
  261. // AppendObject the parameter appendPosition specifies which postion (in the target object) to append. For the first append (to a non-existing file),
  262. // the appendPosition should be 0. The appendPosition in the subsequent calls will be the current object length.
  263. // For example, the first appendObject's appendPosition is 0 and it uploaded 65536 bytes data, then the second call's position is 65536.
  264. // The response header x-oss-next-append-position after each successful request also specifies the next call's append position (so the caller need not to maintain this information).
  265. //
  266. // objectKey the target object to append to.
  267. // reader io.Reader. The read instance for reading the data to append.
  268. // appendPosition the start position to append.
  269. // destObjectProperties the options for the first appending, such as CacheControl, ContentDisposition, ContentEncoding,
  270. // Expires, ServerSideEncryption, ObjectACL.
  271. //
  272. // int64 the next append position, it's valid when error is nil.
  273. // error it's nil if no error, otherwise it's an error object.
  274. //
  275. func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
  276. request := &AppendObjectRequest{
  277. ObjectKey: objectKey,
  278. Reader: reader,
  279. Position: appendPosition,
  280. }
  281. result, err := bucket.DoAppendObject(request, options)
  282. if err != nil {
  283. return appendPosition, err
  284. }
  285. return result.NextPosition, err
  286. }
  287. // DoAppendObject is the actual API that does the object append.
  288. //
  289. // request the request object for appending object.
  290. // options the options for appending object.
  291. //
  292. // AppendObjectResult the result object for appending object.
  293. // error it's nil if no error, otherwise it's an error object.
  294. //
  295. func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Option) (*AppendObjectResult, error) {
  296. params := map[string]interface{}{}
  297. params["append"] = nil
  298. params["position"] = strconv.FormatInt(request.Position, 10)
  299. headers := make(map[string]string)
  300. opts := addContentType(options, request.ObjectKey)
  301. handleOptions(headers, opts)
  302. var initCRC uint64
  303. isCRCSet, initCRCOpt, _ := isOptionSet(options, initCRC64)
  304. if isCRCSet {
  305. initCRC = initCRCOpt.(uint64)
  306. }
  307. listener := getProgressListener(options)
  308. handleOptions(headers, opts)
  309. resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, headers,
  310. request.Reader, initCRC, listener)
  311. if err != nil {
  312. return nil, err
  313. }
  314. defer resp.Body.Close()
  315. nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
  316. result := &AppendObjectResult{
  317. NextPosition: nextPosition,
  318. CRC: resp.ServerCRC,
  319. }
  320. if bucket.getConfig().IsEnableCRC && isCRCSet {
  321. err = checkCRC(resp, "AppendObject")
  322. if err != nil {
  323. return result, err
  324. }
  325. }
  326. return result, nil
  327. }
  328. // DeleteObject deletes the object.
  329. //
  330. // objectKey the object key to delete.
  331. //
  332. // error it's nil if no error, otherwise it's an error object.
  333. //
  334. func (bucket Bucket) DeleteObject(objectKey string) error {
  335. params := map[string]interface{}{}
  336. resp, err := bucket.do("DELETE", objectKey, params, nil, nil, nil)
  337. if err != nil {
  338. return err
  339. }
  340. defer resp.Body.Close()
  341. return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
  342. }
  343. // DeleteObjects deletes multiple objects.
  344. //
  345. // objectKeys the object keys to delete.
  346. // options the options for deleting objects.
  347. // Supported option is DeleteObjectsQuiet which means it will not return error even deletion failed (not recommended). By default it's not used.
  348. //
  349. // DeleteObjectsResult the result object.
  350. // error it's nil if no error, otherwise it's an error object.
  351. //
  352. func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
  353. out := DeleteObjectsResult{}
  354. dxml := deleteXML{}
  355. for _, key := range objectKeys {
  356. dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
  357. }
  358. isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
  359. dxml.Quiet = isQuiet.(bool)
  360. bs, err := xml.Marshal(dxml)
  361. if err != nil {
  362. return out, err
  363. }
  364. buffer := new(bytes.Buffer)
  365. buffer.Write(bs)
  366. contentType := http.DetectContentType(buffer.Bytes())
  367. options = append(options, ContentType(contentType))
  368. sum := md5.Sum(bs)
  369. b64 := base64.StdEncoding.EncodeToString(sum[:])
  370. options = append(options, ContentMD5(b64))
  371. params := map[string]interface{}{}
  372. params["delete"] = nil
  373. params["encoding-type"] = "url"
  374. resp, err := bucket.do("POST", "", params, options, buffer, nil)
  375. if err != nil {
  376. return out, err
  377. }
  378. defer resp.Body.Close()
  379. if !dxml.Quiet {
  380. if err = xmlUnmarshal(resp.Body, &out); err == nil {
  381. err = decodeDeleteObjectsResult(&out)
  382. }
  383. }
  384. return out, err
  385. }
  386. // IsObjectExist checks if the object exists.
  387. //
  388. // bool flag of object's existence (true:exists; false:non-exist) when error is nil.
  389. //
  390. // error it's nil if no error, otherwise it's an error object.
  391. //
  392. func (bucket Bucket) IsObjectExist(objectKey string) (bool, error) {
  393. _, err := bucket.GetObjectMeta(objectKey)
  394. if err == nil {
  395. return true, nil
  396. }
  397. switch err.(type) {
  398. case ServiceError:
  399. if err.(ServiceError).StatusCode == 404 && err.(ServiceError).Code == "NoSuchKey" {
  400. return false, nil
  401. }
  402. }
  403. return false, err
  404. }
  405. // ListObjects lists the objects under the current bucket.
  406. //
  407. // options it contains all the filters for listing objects.
  408. // It could specify a prefix filter on object keys, the max keys count to return and the object key marker and the delimiter for grouping object names.
  409. // The key marker means the returned objects' key must be greater than it in lexicographic order.
  410. //
  411. // For example, if the bucket has 8 objects, my-object-1, my-object-11, my-object-2, my-object-21,
  412. // my-object-22, my-object-3, my-object-31, my-object-32. If the prefix is my-object-2 (no other filters), then it returns
  413. // my-object-2, my-object-21, my-object-22 three objects. If the marker is my-object-22 (no other filters), then it returns
  414. // my-object-3, my-object-31, my-object-32 three objects. If the max keys is 5, then it returns 5 objects.
  415. // The three filters could be used together to achieve filter and paging functionality.
  416. // If the prefix is the folder name, then it could list all files under this folder (including the files under its subfolders).
  417. // But if the delimiter is specified with '/', then it only returns that folder's files (no subfolder's files). The direct subfolders are in the commonPrefixes properties.
  418. // For example, if the bucket has three objects fun/test.jpg, fun/movie/001.avi, fun/movie/007.avi. And if the prefix is "fun/", then it returns all three objects.
  419. // But if the delimiter is '/', then only "fun/test.jpg" is returned as files and fun/movie/ is returned as common prefix.
  420. //
  421. // For common usage scenario, check out sample/list_object.go.
  422. //
  423. // ListObjectsResponse the return value after operation succeeds (only valid when error is nil).
  424. //
  425. func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
  426. var out ListObjectsResult
  427. options = append(options, EncodingType("url"))
  428. params, err := getRawParams(options)
  429. if err != nil {
  430. return out, err
  431. }
  432. resp, err := bucket.do("GET", "", params, nil, nil, nil)
  433. if err != nil {
  434. return out, err
  435. }
  436. defer resp.Body.Close()
  437. err = xmlUnmarshal(resp.Body, &out)
  438. if err != nil {
  439. return out, err
  440. }
  441. err = decodeListObjectsResult(&out)
  442. return out, err
  443. }
  444. // SetObjectMeta sets the metadata of the Object.
  445. //
  446. // objectKey object
  447. // options options for setting the metadata. The valid options are CacheControl, ContentDisposition, ContentEncoding, Expires,
  448. // ServerSideEncryption, and custom metadata.
  449. //
  450. // error it's nil if no error, otherwise it's an error object.
  451. //
  452. func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
  453. options = append(options, MetadataDirective(MetaReplace))
  454. _, err := bucket.CopyObject(objectKey, objectKey, options...)
  455. return err
  456. }
  457. // GetObjectDetailedMeta gets the object's detailed metadata
  458. //
  459. // objectKey object key.
  460. // options the constraints of the object. Only when the object meets the requirements this method will return the metadata. Otherwise returns error. Valid options are IfModifiedSince, IfUnmodifiedSince,
  461. // IfMatch, IfNoneMatch. For more details check out https://help.aliyun.com/document_detail/oss/api-reference/object/HeadObject.html
  462. //
  463. // http.Header object meta when error is nil.
  464. // error it's nil if no error, otherwise it's an error object.
  465. //
  466. func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
  467. params := map[string]interface{}{}
  468. resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
  469. if err != nil {
  470. return nil, err
  471. }
  472. defer resp.Body.Close()
  473. return resp.Headers, nil
  474. }
  475. // GetObjectMeta gets object metadata.
  476. //
  477. // GetObjectMeta is more lightweight than GetObjectDetailedMeta as it only returns basic metadata including ETag
  478. // size, LastModified. The size information is in the HTTP header Content-Length.
  479. //
  480. // objectKey object key
  481. //
  482. // http.Header the object's metadata, valid when error is nil.
  483. // error it's nil if no error, otherwise it's an error object.
  484. //
  485. func (bucket Bucket) GetObjectMeta(objectKey string) (http.Header, error) {
  486. params := map[string]interface{}{}
  487. params["objectMeta"] = nil
  488. //resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil, nil)
  489. resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
  490. if err != nil {
  491. return nil, err
  492. }
  493. defer resp.Body.Close()
  494. return resp.Headers, nil
  495. }
  496. // SetObjectACL updates the object's ACL.
  497. //
  498. // Only the bucket's owner could update object's ACL which priority is higher than bucket's ACL.
  499. // For example, if the bucket ACL is private and object's ACL is public-read-write.
  500. // Then object's ACL is used and it means all users could read or write that object.
  501. // When the object's ACL is not set, then bucket's ACL is used as the object's ACL.
  502. //
  503. // Object read operations include GetObject, HeadObject, CopyObject and UploadPartCopy on the source object;
  504. // Object write operations include PutObject, PostObject, AppendObject, DeleteObject, DeleteMultipleObjects,
  505. // CompleteMultipartUpload and CopyObject on target object.
  506. //
  507. // objectKey the target object key (to set the ACL on)
  508. // objectAcl object ACL. Valid options are PrivateACL, PublicReadACL, PublicReadWriteACL.
  509. //
  510. // error it's nil if no error, otherwise it's an error object.
  511. //
  512. func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
  513. options := []Option{ObjectACL(objectACL)}
  514. params := map[string]interface{}{}
  515. params["acl"] = nil
  516. resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
  517. if err != nil {
  518. return err
  519. }
  520. defer resp.Body.Close()
  521. return checkRespCode(resp.StatusCode, []int{http.StatusOK})
  522. }
  523. // GetObjectACL gets object's ACL
  524. //
  525. // objectKey the object to get ACL from.
  526. //
  527. // GetObjectACLResult the result object when error is nil. GetObjectACLResult.Acl is the object ACL.
  528. // error it's nil if no error, otherwise it's an error object.
  529. //
  530. func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error) {
  531. var out GetObjectACLResult
  532. params := map[string]interface{}{}
  533. params["acl"] = nil
  534. resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
  535. if err != nil {
  536. return out, err
  537. }
  538. defer resp.Body.Close()
  539. err = xmlUnmarshal(resp.Body, &out)
  540. return out, err
  541. }
  542. // PutSymlink creates a symlink (to point to an existing object)
  543. //
  544. // Symlink cannot point to another symlink.
  545. // When creating a symlink, it does not check the existence of the target file, and does not check if the target file is symlink.
  546. // Neither it checks the caller's permission on the target file. All these checks are deferred to the actual GetObject call via this symlink.
  547. // If trying to add an existing file, as long as the caller has the write permission, the existing one will be overwritten.
  548. // If the x-oss-meta- is specified, it will be added as the metadata of the symlink file.
  549. //
  550. // symObjectKey the symlink object's key.
  551. // targetObjectKey the target object key to point to.
  552. //
  553. // error it's nil if no error, otherwise it's an error object.
  554. //
  555. func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
  556. options = append(options, symlinkTarget(url.QueryEscape(targetObjectKey)))
  557. params := map[string]interface{}{}
  558. params["symlink"] = nil
  559. resp, err := bucket.do("PUT", symObjectKey, params, options, nil, nil)
  560. if err != nil {
  561. return err
  562. }
  563. defer resp.Body.Close()
  564. return checkRespCode(resp.StatusCode, []int{http.StatusOK})
  565. }
  566. // GetSymlink gets the symlink object with the specified key.
  567. // If the symlink object does not exist, returns 404.
  568. //
  569. // objectKey the symlink object's key.
  570. //
  571. // error it's nil if no error, otherwise it's an error object.
  572. // When error is nil, the target file key is in the X-Oss-Symlink-Target header of the returned object.
  573. //
  574. func (bucket Bucket) GetSymlink(objectKey string) (http.Header, error) {
  575. params := map[string]interface{}{}
  576. params["symlink"] = nil
  577. resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
  578. if err != nil {
  579. return nil, err
  580. }
  581. defer resp.Body.Close()
  582. targetObjectKey := resp.Headers.Get(HTTPHeaderOssSymlinkTarget)
  583. targetObjectKey, err = url.QueryUnescape(targetObjectKey)
  584. if err != nil {
  585. return resp.Headers, err
  586. }
  587. resp.Headers.Set(HTTPHeaderOssSymlinkTarget, targetObjectKey)
  588. return resp.Headers, err
  589. }
  590. // RestoreObject restores the object from the archive storage.
  591. //
  592. // An archive object is in cold status by default and it cannot be accessed.
  593. // When restore is called on the cold object, it will become available for access after some time.
  594. // If multiple restores are called on the same file when the object is being restored, server side does nothing for additional calls but returns success.
  595. // By default, the restored object is available for access for one day. After that it will be unavailable again.
  596. // But if another RestoreObject are called after the file is restored, then it will extend one day's access time of that object, up to 7 days.
  597. //
  598. // objectKey object key to restore.
  599. //
  600. // error it's nil if no error, otherwise it's an error object.
  601. //
  602. func (bucket Bucket) RestoreObject(objectKey string) error {
  603. params := map[string]interface{}{}
  604. params["restore"] = nil
  605. resp, err := bucket.do("POST", objectKey, params, nil, nil, nil)
  606. if err != nil {
  607. return err
  608. }
  609. defer resp.Body.Close()
  610. return checkRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
  611. }
  612. // SignURL signs the URL. Users could access the object directly with this URL without getting the AK.
  613. //
  614. // objectKey the target object to sign.
  615. // signURLConfig the configuration for the signed URL
  616. //
  617. // string returns the signed URL, when error is nil.
  618. // error it's nil if no error, otherwise it's an error object.
  619. //
  620. func (bucket Bucket) SignURL(objectKey string, method HTTPMethod, expiredInSec int64, options ...Option) (string, error) {
  621. if expiredInSec < 0 {
  622. return "", fmt.Errorf("invalid expires: %d, expires must bigger than 0", expiredInSec)
  623. }
  624. expiration := time.Now().Unix() + expiredInSec
  625. params, err := getRawParams(options)
  626. if err != nil {
  627. return "", err
  628. }
  629. headers := make(map[string]string)
  630. err = handleOptions(headers, options)
  631. if err != nil {
  632. return "", err
  633. }
  634. return bucket.Client.Conn.signURL(method, bucket.BucketName, objectKey, expiration, params, headers), nil
  635. }
  636. // PutObjectWithURL uploads an object with the URL. If the object exists, it will be overwritten.
  637. // PutObjectWithURL It will not generate minetype according to the key name.
  638. //
  639. // signedURL signed URL.
  640. // reader io.Reader the read instance for reading the data for the upload.
  641. // options the options for uploading the data. The valid options are CacheControl, ContentDisposition, ContentEncoding,
  642. // Expires, ServerSideEncryption, ObjectACL and custom metadata. Check out the following link for details:
  643. // https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
  644. //
  645. // error it's nil if no error, otherwise it's an error object.
  646. //
  647. func (bucket Bucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...Option) error {
  648. resp, err := bucket.DoPutObjectWithURL(signedURL, reader, options)
  649. if err != nil {
  650. return err
  651. }
  652. defer resp.Body.Close()
  653. return err
  654. }
  655. // PutObjectFromFileWithURL uploads an object from a local file with the signed URL.
  656. // PutObjectFromFileWithURL It does not generate mimetype according to object key's name or the local file name.
  657. //
  658. // signedURL the signed URL.
  659. // filePath local file path, such as dirfile.txt, for uploading.
  660. // options options for uploading, same as the options in PutObject function.
  661. //
  662. // error it's nil if no error, otherwise it's an error object.
  663. //
  664. func (bucket Bucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...Option) error {
  665. fd, err := os.Open(filePath)
  666. if err != nil {
  667. return err
  668. }
  669. defer fd.Close()
  670. resp, err := bucket.DoPutObjectWithURL(signedURL, fd, options)
  671. if err != nil {
  672. return err
  673. }
  674. defer resp.Body.Close()
  675. return err
  676. }
  677. // DoPutObjectWithURL is the actual API that does the upload with URL work(internal for SDK)
  678. //
  679. // signedURL the signed URL.
  680. // reader io.Reader the read instance for getting the data to upload.
  681. // options options for uploading.
  682. //
  683. // Response the response object which contains the HTTP response.
  684. // error it's nil if no error, otherwise it's an error object.
  685. //
  686. func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []Option) (*Response, error) {
  687. listener := getProgressListener(options)
  688. params := map[string]interface{}{}
  689. resp, err := bucket.doURL("PUT", signedURL, params, options, reader, listener)
  690. if err != nil {
  691. return nil, err
  692. }
  693. if bucket.getConfig().IsEnableCRC {
  694. err = checkCRC(resp, "DoPutObjectWithURL")
  695. if err != nil {
  696. return resp, err
  697. }
  698. }
  699. err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
  700. return resp, err
  701. }
  702. // GetObjectWithURL downloads the object and returns the reader instance, with the signed URL.
  703. //
  704. // signedURL the signed URL.
  705. // options options for downloading the object. Valid options are IfModifiedSince, IfUnmodifiedSince, IfMatch,
  706. // IfNoneMatch, AcceptEncoding. For more information, check out the following link:
  707. // https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
  708. //
  709. // io.ReadCloser the reader object for getting the data from response. It needs be closed after the usage. It's only valid when error is nil.
  710. // error it's nil if no error, otherwise it's an error object.
  711. //
  712. func (bucket Bucket) GetObjectWithURL(signedURL string, options ...Option) (io.ReadCloser, error) {
  713. result, err := bucket.DoGetObjectWithURL(signedURL, options)
  714. if err != nil {
  715. return nil, err
  716. }
  717. return result.Response.Body, nil
  718. }
  719. // GetObjectToFileWithURL downloads the object into a local file with the signed URL.
  720. //
  721. // signedURL the signed URL
  722. // filePath the local file path to download to.
  723. // options the options for downloading object. Check out the parameter options in function GetObject for the reference.
  724. //
  725. // error it's nil if no error, otherwise it's an error object.
  726. //
  727. func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options ...Option) error {
  728. tempFilePath := filePath + TempFileSuffix
  729. // Get the object's content
  730. result, err := bucket.DoGetObjectWithURL(signedURL, options)
  731. if err != nil {
  732. return err
  733. }
  734. defer result.Response.Body.Close()
  735. // If the file does not exist, create one. If exists, then overwrite it.
  736. fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
  737. if err != nil {
  738. return err
  739. }
  740. // Save the data to the file.
  741. _, err = io.Copy(fd, result.Response.Body)
  742. fd.Close()
  743. if err != nil {
  744. return err
  745. }
  746. // Compare the CRC value. If CRC values do not match, return error.
  747. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  748. if bucket.getConfig().IsEnableCRC && !hasRange {
  749. result.Response.ClientCRC = result.ClientCRC.Sum64()
  750. err = checkCRC(result.Response, "GetObjectToFileWithURL")
  751. if err != nil {
  752. os.Remove(tempFilePath)
  753. return err
  754. }
  755. }
  756. return os.Rename(tempFilePath, filePath)
  757. }
  758. // DoGetObjectWithURL is the actual API that downloads the file with the signed URL.
  759. //
  760. // signedURL the signed URL.
  761. // options the options for getting object. Check out parameter options in GetObject for the reference.
  762. //
  763. // GetObjectResult the result object when the error is nil.
  764. // error it's nil if no error, otherwise it's an error object.
  765. //
  766. func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*GetObjectResult, error) {
  767. params := map[string]interface{}{}
  768. resp, err := bucket.doURL("GET", signedURL, params, options, nil, nil)
  769. if err != nil {
  770. return nil, err
  771. }
  772. result := &GetObjectResult{
  773. Response: resp,
  774. }
  775. // CRC
  776. var crcCalc hash.Hash64
  777. hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
  778. if bucket.getConfig().IsEnableCRC && !hasRange {
  779. crcCalc = crc64.New(crcTable())
  780. result.ServerCRC = resp.ServerCRC
  781. result.ClientCRC = crcCalc
  782. }
  783. // Progress
  784. listener := getProgressListener(options)
  785. contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
  786. resp.Body = ioutil.NopCloser(TeeReader(resp.Body, crcCalc, contentLen, listener, nil))
  787. return result, nil
  788. }
  789. // Private
  790. func (bucket Bucket) do(method, objectName string, params map[string]interface{}, options []Option,
  791. data io.Reader, listener ProgressListener) (*Response, error) {
  792. headers := make(map[string]string)
  793. err := handleOptions(headers, options)
  794. if err != nil {
  795. return nil, err
  796. }
  797. return bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
  798. params, headers, data, 0, listener)
  799. }
  800. func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[string]interface{}, options []Option,
  801. data io.Reader, listener ProgressListener) (*Response, error) {
  802. headers := make(map[string]string)
  803. err := handleOptions(headers, options)
  804. if err != nil {
  805. return nil, err
  806. }
  807. return bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)
  808. }
  809. func (bucket Bucket) getConfig() *Config {
  810. return bucket.Client.Config
  811. }
  812. func addContentType(options []Option, keys ...string) []Option {
  813. typ := TypeByExtension("")
  814. for _, key := range keys {
  815. typ = TypeByExtension(key)
  816. if typ != "" {
  817. break
  818. }
  819. }
  820. if typ == "" {
  821. typ = "application/octet-stream"
  822. }
  823. opts := []Option{ContentType(typ)}
  824. opts = append(opts, options...)
  825. return opts
  826. }