bucket.go 35 KB

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