blockblob.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. package storage
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "bytes"
  17. "encoding/xml"
  18. "fmt"
  19. "io"
  20. "net/http"
  21. "net/url"
  22. "strconv"
  23. "strings"
  24. "time"
  25. )
  26. // BlockListType is used to filter out types of blocks in a Get Blocks List call
  27. // for a block blob.
  28. //
  29. // See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx for all
  30. // block types.
  31. type BlockListType string
  32. // Filters for listing blocks in block blobs
  33. const (
  34. BlockListTypeAll BlockListType = "all"
  35. BlockListTypeCommitted BlockListType = "committed"
  36. BlockListTypeUncommitted BlockListType = "uncommitted"
  37. )
  38. // Maximum sizes (per REST API) for various concepts
  39. const (
  40. MaxBlobBlockSize = 100 * 1024 * 1024
  41. MaxBlobPageSize = 4 * 1024 * 1024
  42. )
  43. // BlockStatus defines states a block for a block blob can
  44. // be in.
  45. type BlockStatus string
  46. // List of statuses that can be used to refer to a block in a block list
  47. const (
  48. BlockStatusUncommitted BlockStatus = "Uncommitted"
  49. BlockStatusCommitted BlockStatus = "Committed"
  50. BlockStatusLatest BlockStatus = "Latest"
  51. )
  52. // Block is used to create Block entities for Put Block List
  53. // call.
  54. type Block struct {
  55. ID string
  56. Status BlockStatus
  57. }
  58. // BlockListResponse contains the response fields from Get Block List call.
  59. //
  60. // See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx
  61. type BlockListResponse struct {
  62. XMLName xml.Name `xml:"BlockList"`
  63. CommittedBlocks []BlockResponse `xml:"CommittedBlocks>Block"`
  64. UncommittedBlocks []BlockResponse `xml:"UncommittedBlocks>Block"`
  65. }
  66. // BlockResponse contains the block information returned
  67. // in the GetBlockListCall.
  68. type BlockResponse struct {
  69. Name string `xml:"Name"`
  70. Size int64 `xml:"Size"`
  71. }
  72. // CreateBlockBlob initializes an empty block blob with no blocks.
  73. //
  74. // See CreateBlockBlobFromReader for more info on creating blobs.
  75. //
  76. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
  77. func (b *Blob) CreateBlockBlob(options *PutBlobOptions) error {
  78. return b.CreateBlockBlobFromReader(nil, options)
  79. }
  80. // CreateBlockBlobFromReader initializes a block blob using data from
  81. // reader. Size must be the number of bytes read from reader. To
  82. // create an empty blob, use size==0 and reader==nil.
  83. //
  84. // Any headers set in blob.Properties or metadata in blob.Metadata
  85. // will be set on the blob.
  86. //
  87. // The API rejects requests with size > 256 MiB (but this limit is not
  88. // checked by the SDK). To write a larger blob, use CreateBlockBlob,
  89. // PutBlock, and PutBlockList.
  90. //
  91. // To create a blob from scratch, call container.GetBlobReference() to
  92. // get an empty blob, fill in blob.Properties and blob.Metadata as
  93. // appropriate then call this method.
  94. //
  95. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
  96. func (b *Blob) CreateBlockBlobFromReader(blob io.Reader, options *PutBlobOptions) error {
  97. params := url.Values{}
  98. headers := b.Container.bsc.client.getStandardHeaders()
  99. headers["x-ms-blob-type"] = string(BlobTypeBlock)
  100. headers["Content-Length"] = "0"
  101. var n int64
  102. var err error
  103. if blob != nil {
  104. type lener interface {
  105. Len() int
  106. }
  107. // TODO(rjeczalik): handle io.ReadSeeker, in case blob is *os.File etc.
  108. if l, ok := blob.(lener); ok {
  109. n = int64(l.Len())
  110. } else {
  111. var buf bytes.Buffer
  112. n, err = io.Copy(&buf, blob)
  113. if err != nil {
  114. return err
  115. }
  116. blob = &buf
  117. }
  118. headers["Content-Length"] = strconv.FormatInt(n, 10)
  119. }
  120. b.Properties.ContentLength = n
  121. headers = mergeHeaders(headers, headersFromStruct(b.Properties))
  122. headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
  123. if options != nil {
  124. params = addTimeout(params, options.Timeout)
  125. headers = mergeHeaders(headers, headersFromStruct(*options))
  126. }
  127. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
  128. resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
  129. if err != nil {
  130. return err
  131. }
  132. return b.respondCreation(resp, BlobTypeBlock)
  133. }
  134. // PutBlockOptions includes the options for a put block operation
  135. type PutBlockOptions struct {
  136. Timeout uint
  137. LeaseID string `header:"x-ms-lease-id"`
  138. ContentMD5 string `header:"Content-MD5"`
  139. RequestID string `header:"x-ms-client-request-id"`
  140. }
  141. // PutBlock saves the given data chunk to the specified block blob with
  142. // given ID.
  143. //
  144. // The API rejects chunks larger than 100 MiB (but this limit is not
  145. // checked by the SDK).
  146. //
  147. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
  148. func (b *Blob) PutBlock(blockID string, chunk []byte, options *PutBlockOptions) error {
  149. return b.PutBlockWithLength(blockID, uint64(len(chunk)), bytes.NewReader(chunk), options)
  150. }
  151. // PutBlockWithLength saves the given data stream of exactly specified size to
  152. // the block blob with given ID. It is an alternative to PutBlocks where data
  153. // comes as stream but the length is known in advance.
  154. //
  155. // The API rejects requests with size > 100 MiB (but this limit is not
  156. // checked by the SDK).
  157. //
  158. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
  159. func (b *Blob) PutBlockWithLength(blockID string, size uint64, blob io.Reader, options *PutBlockOptions) error {
  160. query := url.Values{
  161. "comp": {"block"},
  162. "blockid": {blockID},
  163. }
  164. headers := b.Container.bsc.client.getStandardHeaders()
  165. headers["Content-Length"] = fmt.Sprintf("%v", size)
  166. if options != nil {
  167. query = addTimeout(query, options.Timeout)
  168. headers = mergeHeaders(headers, headersFromStruct(*options))
  169. }
  170. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), query)
  171. resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
  172. if err != nil {
  173. return err
  174. }
  175. return b.respondCreation(resp, BlobTypeBlock)
  176. }
  177. // PutBlockListOptions includes the options for a put block list operation
  178. type PutBlockListOptions struct {
  179. Timeout uint
  180. LeaseID string `header:"x-ms-lease-id"`
  181. IfModifiedSince *time.Time `header:"If-Modified-Since"`
  182. IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
  183. IfMatch string `header:"If-Match"`
  184. IfNoneMatch string `header:"If-None-Match"`
  185. RequestID string `header:"x-ms-client-request-id"`
  186. }
  187. // PutBlockList saves list of blocks to the specified block blob.
  188. //
  189. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block-List
  190. func (b *Blob) PutBlockList(blocks []Block, options *PutBlockListOptions) error {
  191. params := url.Values{"comp": {"blocklist"}}
  192. blockListXML := prepareBlockListRequest(blocks)
  193. headers := b.Container.bsc.client.getStandardHeaders()
  194. headers["Content-Length"] = fmt.Sprintf("%v", len(blockListXML))
  195. headers = mergeHeaders(headers, headersFromStruct(b.Properties))
  196. headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
  197. if options != nil {
  198. params = addTimeout(params, options.Timeout)
  199. headers = mergeHeaders(headers, headersFromStruct(*options))
  200. }
  201. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
  202. resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, strings.NewReader(blockListXML), b.Container.bsc.auth)
  203. if err != nil {
  204. return err
  205. }
  206. defer drainRespBody(resp)
  207. return checkRespCode(resp, []int{http.StatusCreated})
  208. }
  209. // GetBlockListOptions includes the options for a get block list operation
  210. type GetBlockListOptions struct {
  211. Timeout uint
  212. Snapshot *time.Time
  213. LeaseID string `header:"x-ms-lease-id"`
  214. RequestID string `header:"x-ms-client-request-id"`
  215. }
  216. // GetBlockList retrieves list of blocks in the specified block blob.
  217. //
  218. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Block-List
  219. func (b *Blob) GetBlockList(blockType BlockListType, options *GetBlockListOptions) (BlockListResponse, error) {
  220. params := url.Values{
  221. "comp": {"blocklist"},
  222. "blocklisttype": {string(blockType)},
  223. }
  224. headers := b.Container.bsc.client.getStandardHeaders()
  225. if options != nil {
  226. params = addTimeout(params, options.Timeout)
  227. params = addSnapshot(params, options.Snapshot)
  228. headers = mergeHeaders(headers, headersFromStruct(*options))
  229. }
  230. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
  231. var out BlockListResponse
  232. resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
  233. if err != nil {
  234. return out, err
  235. }
  236. defer resp.Body.Close()
  237. err = xmlUnmarshal(resp.Body, &out)
  238. return out, err
  239. }