bucket.go 32 KB

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