comparison venv/lib/python2.7/site-packages/boto/gs/key.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
1 # Copyright 2010 Google Inc.
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a
4 # copy of this software and associated documentation files (the
5 # "Software"), to deal in the Software without restriction, including
6 # without limitation the rights to use, copy, modify, merge, publish, dis-
7 # tribute, sublicense, and/or sell copies of the Software, and to permit
8 # persons to whom the Software is furnished to do so, subject to the fol-
9 # lowing conditions:
10 #
11 # The above copyright notice and this permission notice shall be included
12 # in all copies or substantial portions of the Software.
13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 # IN THE SOFTWARE.
21
22 import base64
23 import binascii
24 import os
25 import re
26
27 from boto.compat import StringIO
28 from boto.exception import BotoClientError
29 from boto.s3.key import Key as S3Key
30 from boto.s3.keyfile import KeyFile
31 from boto.utils import compute_hash
32 from boto.utils import get_utf8_value
33
34 class Key(S3Key):
35 """
36 Represents a key (object) in a GS bucket.
37
38 :ivar bucket: The parent :class:`boto.gs.bucket.Bucket`.
39 :ivar name: The name of this Key object.
40 :ivar metadata: A dictionary containing user metadata that you
41 wish to store with the object or that has been retrieved from
42 an existing object.
43 :ivar cache_control: The value of the `Cache-Control` HTTP header.
44 :ivar content_type: The value of the `Content-Type` HTTP header.
45 :ivar content_encoding: The value of the `Content-Encoding` HTTP header.
46 :ivar content_disposition: The value of the `Content-Disposition` HTTP
47 header.
48 :ivar content_language: The value of the `Content-Language` HTTP header.
49 :ivar etag: The `etag` associated with this object.
50 :ivar last_modified: The string timestamp representing the last
51 time this object was modified in GS.
52 :ivar owner: The ID of the owner of this object.
53 :ivar storage_class: The storage class of the object. Currently, one of:
54 STANDARD | DURABLE_REDUCED_AVAILABILITY.
55 :ivar md5: The MD5 hash of the contents of the object.
56 :ivar size: The size, in bytes, of the object.
57 :ivar generation: The generation number of the object.
58 :ivar metageneration: The generation number of the object metadata.
59 :ivar encrypted: Whether the object is encrypted while at rest on
60 the server.
61 :ivar cloud_hashes: Dictionary of checksums as supplied by the storage
62 provider.
63 """
64
65 def __init__(self, bucket=None, name=None, generation=None):
66 super(Key, self).__init__(bucket=bucket, name=name)
67 self.generation = generation
68 self.meta_generation = None
69 self.cloud_hashes = {}
70 self.component_count = None
71
72 def __repr__(self):
73 if self.generation and self.metageneration:
74 ver_str = '#%s.%s' % (self.generation, self.metageneration)
75 else:
76 ver_str = ''
77 if self.bucket:
78 return '<Key: %s,%s%s>' % (self.bucket.name, self.name, ver_str)
79 else:
80 return '<Key: None,%s%s>' % (self.name, ver_str)
81
82 def endElement(self, name, value, connection):
83 if name == 'Key':
84 self.name = value
85 elif name == 'ETag':
86 self.etag = value
87 elif name == 'IsLatest':
88 if value == 'true':
89 self.is_latest = True
90 else:
91 self.is_latest = False
92 elif name == 'LastModified':
93 self.last_modified = value
94 elif name == 'Size':
95 self.size = int(value)
96 elif name == 'StorageClass':
97 self.storage_class = value
98 elif name == 'Owner':
99 pass
100 elif name == 'VersionId':
101 self.version_id = value
102 elif name == 'Generation':
103 self.generation = value
104 elif name == 'MetaGeneration':
105 self.metageneration = value
106 else:
107 setattr(self, name, value)
108
109 def handle_version_headers(self, resp, force=False):
110 self.metageneration = resp.getheader('x-goog-metageneration', None)
111 self.generation = resp.getheader('x-goog-generation', None)
112
113 def handle_restore_headers(self, response):
114 return
115
116 def handle_addl_headers(self, headers):
117 for key, value in headers:
118 if key == 'x-goog-hash':
119 for hash_pair in value.split(','):
120 alg, b64_digest = hash_pair.strip().split('=', 1)
121 self.cloud_hashes[alg] = binascii.a2b_base64(b64_digest)
122 elif key == 'x-goog-component-count':
123 self.component_count = int(value)
124 elif key == 'x-goog-generation':
125 self.generation = value
126 # Use x-goog-stored-content-encoding and
127 # x-goog-stored-content-length to indicate original content length
128 # and encoding, which are transcoding-invariant (so are preferable
129 # over using content-encoding and size headers).
130 elif key == 'x-goog-stored-content-encoding':
131 self.content_encoding = value
132 elif key == 'x-goog-stored-content-length':
133 self.size = int(value)
134
135 def open_read(self, headers=None, query_args='',
136 override_num_retries=None, response_headers=None):
137 """
138 Open this key for reading
139
140 :type headers: dict
141 :param headers: Headers to pass in the web request
142
143 :type query_args: string
144 :param query_args: Arguments to pass in the query string
145 (ie, 'torrent')
146
147 :type override_num_retries: int
148 :param override_num_retries: If not None will override configured
149 num_retries parameter for underlying GET.
150
151 :type response_headers: dict
152 :param response_headers: A dictionary containing HTTP
153 headers/values that will override any headers associated
154 with the stored object in the response. See
155 http://goo.gl/EWOPb for details.
156 """
157 # For GCS we need to include the object generation in the query args.
158 # The rest of the processing is handled in the parent class.
159 if self.generation:
160 if query_args:
161 query_args += '&'
162 query_args += 'generation=%s' % self.generation
163 super(Key, self).open_read(headers=headers, query_args=query_args,
164 override_num_retries=override_num_retries,
165 response_headers=response_headers)
166
167 def get_file(self, fp, headers=None, cb=None, num_cb=10,
168 torrent=False, version_id=None, override_num_retries=None,
169 response_headers=None, hash_algs=None):
170 query_args = None
171 if self.generation:
172 query_args = ['generation=%s' % self.generation]
173 self._get_file_internal(fp, headers=headers, cb=cb, num_cb=num_cb,
174 override_num_retries=override_num_retries,
175 response_headers=response_headers,
176 hash_algs=hash_algs,
177 query_args=query_args)
178
179 def get_contents_to_file(self, fp, headers=None,
180 cb=None, num_cb=10,
181 torrent=False,
182 version_id=None,
183 res_download_handler=None,
184 response_headers=None,
185 hash_algs=None):
186 """
187 Retrieve an object from GCS using the name of the Key object as the
188 key in GCS. Write the contents of the object to the file pointed
189 to by 'fp'.
190
191 :type fp: File -like object
192 :param fp:
193
194 :type headers: dict
195 :param headers: additional HTTP headers that will be sent with
196 the GET request.
197
198 :type cb: function
199 :param cb: a callback function that will be called to report
200 progress on the upload. The callback should accept two
201 integer parameters, the first representing the number of
202 bytes that have been successfully transmitted to GCS and
203 the second representing the size of the to be transmitted
204 object.
205
206 :type cb: int
207 :param num_cb: (optional) If a callback is specified with the
208 cb parameter this parameter determines the granularity of
209 the callback by defining the maximum number of times the
210 callback will be called during the file transfer.
211
212 :type torrent: bool
213 :param torrent: If True, returns the contents of a torrent
214 file as a string.
215
216 :type res_upload_handler: ResumableDownloadHandler
217 :param res_download_handler: If provided, this handler will
218 perform the download.
219
220 :type response_headers: dict
221 :param response_headers: A dictionary containing HTTP
222 headers/values that will override any headers associated
223 with the stored object in the response. See
224 http://goo.gl/sMkcC for details.
225 """
226 if self.bucket is not None:
227 if res_download_handler:
228 res_download_handler.get_file(self, fp, headers, cb, num_cb,
229 torrent=torrent,
230 version_id=version_id,
231 hash_algs=hash_algs)
232 else:
233 self.get_file(fp, headers, cb, num_cb, torrent=torrent,
234 version_id=version_id,
235 response_headers=response_headers,
236 hash_algs=hash_algs)
237
238 def compute_hash(self, fp, algorithm, size=None):
239 """
240 :type fp: file
241 :param fp: File pointer to the file to hash. The file
242 pointer will be reset to the same position before the
243 method returns.
244
245 :type algorithm: zero-argument constructor for hash objects that
246 implements update() and digest() (e.g. hashlib.md5)
247
248 :type size: int
249 :param size: (optional) The Maximum number of bytes to read
250 from the file pointer (fp). This is useful when uploading
251 a file in multiple parts where the file is being split
252 in place into different parts. Less bytes may be available.
253 """
254 hex_digest, b64_digest, data_size = compute_hash(
255 fp, size=size, hash_algorithm=algorithm)
256 # The internal implementation of compute_hash() needs to return the
257 # data size, but we don't want to return that value to the external
258 # caller because it changes the class interface (i.e. it might
259 # break some code), so we consume the third tuple value here and
260 # return the remainder of the tuple to the caller, thereby preserving
261 # the existing interface.
262 self.size = data_size
263 return (hex_digest, b64_digest)
264
265 def send_file(self, fp, headers=None, cb=None, num_cb=10,
266 query_args=None, chunked_transfer=False, size=None,
267 hash_algs=None):
268 """
269 Upload a file to GCS.
270
271 :type fp: file
272 :param fp: The file pointer to upload. The file pointer must
273 point point at the offset from which you wish to upload.
274 ie. if uploading the full file, it should point at the
275 start of the file. Normally when a file is opened for
276 reading, the fp will point at the first byte. See the
277 bytes parameter below for more info.
278
279 :type headers: dict
280 :param headers: The headers to pass along with the PUT request
281
282 :type num_cb: int
283 :param num_cb: (optional) If a callback is specified with the
284 cb parameter this parameter determines the granularity of
285 the callback by defining the maximum number of times the
286 callback will be called during the file
287 transfer. Providing a negative integer will cause your
288 callback to be called with each buffer read.
289
290 :type query_args: string
291 :param query_args: Arguments to pass in the query string.
292
293 :type chunked_transfer: boolean
294 :param chunked_transfer: (optional) If true, we use chunked
295 Transfer-Encoding.
296
297 :type size: int
298 :param size: (optional) The Maximum number of bytes to read
299 from the file pointer (fp). This is useful when uploading
300 a file in multiple parts where you are splitting the file
301 up into different ranges to be uploaded. If not specified,
302 the default behaviour is to read all bytes from the file
303 pointer. Less bytes may be available.
304
305 :type hash_algs: dictionary
306 :param hash_algs: (optional) Dictionary of hash algorithms and
307 corresponding hashing class that implements update() and digest().
308 Defaults to {'md5': hashlib.md5}.
309 """
310 self._send_file_internal(fp, headers=headers, cb=cb, num_cb=num_cb,
311 query_args=query_args,
312 chunked_transfer=chunked_transfer, size=size,
313 hash_algs=hash_algs)
314
315 def delete(self, headers=None):
316 return self.bucket.delete_key(self.name, version_id=self.version_id,
317 generation=self.generation,
318 headers=headers)
319
320 def add_email_grant(self, permission, email_address):
321 """
322 Convenience method that provides a quick way to add an email grant to a
323 key. This method retrieves the current ACL, creates a new grant based on
324 the parameters passed in, adds that grant to the ACL and then PUT's the
325 new ACL back to GS.
326
327 :type permission: string
328 :param permission: The permission being granted. Should be one of:
329 READ|FULL_CONTROL
330 See http://code.google.com/apis/storage/docs/developer-guide.html#authorization
331 for more details on permissions.
332
333 :type email_address: string
334 :param email_address: The email address associated with the Google
335 account to which you are granting the permission.
336 """
337 acl = self.get_acl()
338 acl.add_email_grant(permission, email_address)
339 self.set_acl(acl)
340
341 def add_user_grant(self, permission, user_id):
342 """
343 Convenience method that provides a quick way to add a canonical user
344 grant to a key. This method retrieves the current ACL, creates a new
345 grant based on the parameters passed in, adds that grant to the ACL and
346 then PUT's the new ACL back to GS.
347
348 :type permission: string
349 :param permission: The permission being granted. Should be one of:
350 READ|FULL_CONTROL
351 See http://code.google.com/apis/storage/docs/developer-guide.html#authorization
352 for more details on permissions.
353
354 :type user_id: string
355 :param user_id: The canonical user id associated with the GS account to
356 which you are granting the permission.
357 """
358 acl = self.get_acl()
359 acl.add_user_grant(permission, user_id)
360 self.set_acl(acl)
361
362 def add_group_email_grant(self, permission, email_address, headers=None):
363 """
364 Convenience method that provides a quick way to add an email group
365 grant to a key. This method retrieves the current ACL, creates a new
366 grant based on the parameters passed in, adds that grant to the ACL and
367 then PUT's the new ACL back to GS.
368
369 :type permission: string
370 :param permission: The permission being granted. Should be one of:
371 READ|FULL_CONTROL
372 See http://code.google.com/apis/storage/docs/developer-guide.html#authorization
373 for more details on permissions.
374
375 :type email_address: string
376 :param email_address: The email address associated with the Google
377 Group to which you are granting the permission.
378 """
379 acl = self.get_acl(headers=headers)
380 acl.add_group_email_grant(permission, email_address)
381 self.set_acl(acl, headers=headers)
382
383 def add_group_grant(self, permission, group_id):
384 """
385 Convenience method that provides a quick way to add a canonical group
386 grant to a key. This method retrieves the current ACL, creates a new
387 grant based on the parameters passed in, adds that grant to the ACL and
388 then PUT's the new ACL back to GS.
389
390 :type permission: string
391 :param permission: The permission being granted. Should be one of:
392 READ|FULL_CONTROL
393 See http://code.google.com/apis/storage/docs/developer-guide.html#authorization
394 for more details on permissions.
395
396 :type group_id: string
397 :param group_id: The canonical group id associated with the Google
398 Groups account you are granting the permission to.
399 """
400 acl = self.get_acl()
401 acl.add_group_grant(permission, group_id)
402 self.set_acl(acl)
403
404 def set_contents_from_file(self, fp, headers=None, replace=True,
405 cb=None, num_cb=10, policy=None, md5=None,
406 res_upload_handler=None, size=None, rewind=False,
407 if_generation=None):
408 """
409 Store an object in GS using the name of the Key object as the
410 key in GS and the contents of the file pointed to by 'fp' as the
411 contents.
412
413 :type fp: file
414 :param fp: The file whose contents are to be uploaded.
415
416 :type headers: dict
417 :param headers: (optional) Additional HTTP headers to be sent with the
418 PUT request.
419
420 :type replace: bool
421 :param replace: (optional) If this parameter is False, the method will
422 first check to see if an object exists in the bucket with the same
423 key. If it does, it won't overwrite it. The default value is True
424 which will overwrite the object.
425
426 :type cb: function
427 :param cb: (optional) Callback function that will be called to report
428 progress on the upload. The callback should accept two integer
429 parameters, the first representing the number of bytes that have
430 been successfully transmitted to GS and the second representing the
431 total number of bytes that need to be transmitted.
432
433 :type num_cb: int
434 :param num_cb: (optional) If a callback is specified with the cb
435 parameter, this parameter determines the granularity of the callback
436 by defining the maximum number of times the callback will be called
437 during the file transfer.
438
439 :type policy: :class:`boto.gs.acl.CannedACLStrings`
440 :param policy: (optional) A canned ACL policy that will be applied to
441 the new key in GS.
442
443 :type md5: tuple
444 :param md5: (optional) A tuple containing the hexdigest version of the
445 MD5 checksum of the file as the first element and the
446 Base64-encoded version of the plain checksum as the second element.
447 This is the same format returned by the compute_md5 method.
448
449 If you need to compute the MD5 for any reason prior to upload, it's
450 silly to have to do it twice so this param, if present, will be
451 used as the MD5 values of the file. Otherwise, the checksum will be
452 computed.
453
454 :type res_upload_handler: :py:class:`boto.gs.resumable_upload_handler.ResumableUploadHandler`
455 :param res_upload_handler: (optional) If provided, this handler will
456 perform the upload.
457
458 :type size: int
459 :param size: (optional) The Maximum number of bytes to read from the
460 file pointer (fp). This is useful when uploading a file in multiple
461 parts where you are splitting the file up into different ranges to
462 be uploaded. If not specified, the default behaviour is to read all
463 bytes from the file pointer. Less bytes may be available.
464
465 Notes:
466
467 1. The "size" parameter currently cannot be used when a
468 resumable upload handler is given but is still useful for
469 uploading part of a file as implemented by the parent class.
470 2. At present Google Cloud Storage does not support multipart
471 uploads.
472
473 :type rewind: bool
474 :param rewind: (optional) If True, the file pointer (fp) will be
475 rewound to the start before any bytes are read from it. The default
476 behaviour is False which reads from the current position of the
477 file pointer (fp).
478
479 :type if_generation: int
480 :param if_generation: (optional) If set to a generation number, the
481 object will only be written to if its current generation number is
482 this value. If set to the value 0, the object will only be written
483 if it doesn't already exist.
484
485 :rtype: int
486 :return: The number of bytes written to the key.
487
488 TODO: At some point we should refactor the Bucket and Key classes,
489 to move functionality common to all providers into a parent class,
490 and provider-specific functionality into subclasses (rather than
491 just overriding/sharing code the way it currently works).
492 """
493 provider = self.bucket.connection.provider
494 if res_upload_handler and size:
495 # could use size instead of file_length if provided but...
496 raise BotoClientError(
497 '"size" param not supported for resumable uploads.')
498 headers = headers or {}
499 if policy:
500 headers[provider.acl_header] = policy
501
502 if rewind:
503 # caller requests reading from beginning of fp.
504 fp.seek(0, os.SEEK_SET)
505 else:
506 # The following seek/tell/seek logic is intended
507 # to detect applications using the older interface to
508 # set_contents_from_file(), which automatically rewound the
509 # file each time the Key was reused. This changed with commit
510 # 14ee2d03f4665fe20d19a85286f78d39d924237e, to support uploads
511 # split into multiple parts and uploaded in parallel, and at
512 # the time of that commit this check was added because otherwise
513 # older programs would get a success status and upload an empty
514 # object. Unfortuantely, it's very inefficient for fp's implemented
515 # by KeyFile (used, for example, by gsutil when copying between
516 # providers). So, we skip the check for the KeyFile case.
517 # TODO: At some point consider removing this seek/tell/seek
518 # logic, after enough time has passed that it's unlikely any
519 # programs remain that assume the older auto-rewind interface.
520 if not isinstance(fp, KeyFile):
521 spos = fp.tell()
522 fp.seek(0, os.SEEK_END)
523 if fp.tell() == spos:
524 fp.seek(0, os.SEEK_SET)
525 if fp.tell() != spos:
526 # Raise an exception as this is likely a programming
527 # error whereby there is data before the fp but nothing
528 # after it.
529 fp.seek(spos)
530 raise AttributeError('fp is at EOF. Use rewind option '
531 'or seek() to data start.')
532 # seek back to the correct position.
533 fp.seek(spos)
534
535 if hasattr(fp, 'name'):
536 self.path = fp.name
537 if self.bucket is not None:
538 if isinstance(fp, KeyFile):
539 # Avoid EOF seek for KeyFile case as it's very inefficient.
540 key = fp.getkey()
541 size = key.size - fp.tell()
542 self.size = size
543 # At present both GCS and S3 use MD5 for the etag for
544 # non-multipart-uploaded objects. If the etag is 32 hex
545 # chars use it as an MD5, to avoid having to read the file
546 # twice while transferring.
547 if (re.match('^"[a-fA-F0-9]{32}"$', key.etag)):
548 etag = key.etag.strip('"')
549 md5 = (etag, base64.b64encode(binascii.unhexlify(etag)))
550 if size:
551 self.size = size
552 else:
553 # If md5 is provided, still need to size so
554 # calculate based on bytes to end of content
555 spos = fp.tell()
556 fp.seek(0, os.SEEK_END)
557 self.size = fp.tell() - spos
558 fp.seek(spos)
559 size = self.size
560
561 if md5 is None:
562 md5 = self.compute_md5(fp, size)
563 self.md5 = md5[0]
564 self.base64md5 = md5[1]
565
566 if self.name is None:
567 self.name = self.md5
568
569 if not replace:
570 if self.bucket.lookup(self.name):
571 return
572
573 if if_generation is not None:
574 headers['x-goog-if-generation-match'] = str(if_generation)
575
576 if res_upload_handler:
577 res_upload_handler.send_file(self, fp, headers, cb, num_cb)
578 else:
579 # Not a resumable transfer so use basic send_file mechanism.
580 self.send_file(fp, headers, cb, num_cb, size=size)
581
582 def set_contents_from_filename(self, filename, headers=None, replace=True,
583 cb=None, num_cb=10, policy=None, md5=None,
584 reduced_redundancy=None,
585 res_upload_handler=None,
586 if_generation=None):
587 """
588 Store an object in GS using the name of the Key object as the
589 key in GS and the contents of the file named by 'filename'.
590 See set_contents_from_file method for details about the
591 parameters.
592
593 :type filename: string
594 :param filename: The name of the file that you want to put onto GS.
595
596 :type headers: dict
597 :param headers: (optional) Additional headers to pass along with the
598 request to GS.
599
600 :type replace: bool
601 :param replace: (optional) If True, replaces the contents of the file
602 if it already exists.
603
604 :type cb: function
605 :param cb: (optional) Callback function that will be called to report
606 progress on the upload. The callback should accept two integer
607 parameters, the first representing the number of bytes that have
608 been successfully transmitted to GS and the second representing the
609 total number of bytes that need to be transmitted.
610
611 :type num_cb: int
612 :param num_cb: (optional) If a callback is specified with the cb
613 parameter this parameter determines the granularity of the callback
614 by defining the maximum number of times the callback will be called
615 during the file transfer.
616
617 :type policy: :py:attribute:`boto.gs.acl.CannedACLStrings`
618 :param policy: (optional) A canned ACL policy that will be applied to
619 the new key in GS.
620
621 :type md5: tuple
622 :param md5: (optional) A tuple containing the hexdigest version of the
623 MD5 checksum of the file as the first element and the
624 Base64-encoded version of the plain checksum as the second element.
625 This is the same format returned by the compute_md5 method.
626
627 If you need to compute the MD5 for any reason prior to upload, it's
628 silly to have to do it twice so this param, if present, will be
629 used as the MD5 values of the file. Otherwise, the checksum will be
630 computed.
631
632 :type res_upload_handler: :py:class:`boto.gs.resumable_upload_handler.ResumableUploadHandler`
633 :param res_upload_handler: (optional) If provided, this handler will
634 perform the upload.
635
636 :type if_generation: int
637 :param if_generation: (optional) If set to a generation number, the
638 object will only be written to if its current generation number is
639 this value. If set to the value 0, the object will only be written
640 if it doesn't already exist.
641 """
642 # Clear out any previously computed hashes, since we are setting the
643 # content.
644 self.local_hashes = {}
645
646 with open(filename, 'rb') as fp:
647 self.set_contents_from_file(fp, headers, replace, cb, num_cb,
648 policy, md5, res_upload_handler,
649 if_generation=if_generation)
650
651 def set_contents_from_string(self, s, headers=None, replace=True,
652 cb=None, num_cb=10, policy=None, md5=None,
653 if_generation=None):
654 """
655 Store an object in GCS using the name of the Key object as the
656 key in GCS and the string 's' as the contents.
657 See set_contents_from_file method for details about the
658 parameters.
659
660 :type headers: dict
661 :param headers: Additional headers to pass along with the
662 request to AWS.
663
664 :type replace: bool
665 :param replace: If True, replaces the contents of the file if
666 it already exists.
667
668 :type cb: function
669 :param cb: a callback function that will be called to report
670 progress on the upload. The callback should accept
671 two integer parameters, the first representing the
672 number of bytes that have been successfully
673 transmitted to GCS and the second representing the
674 size of the to be transmitted object.
675
676 :type cb: int
677 :param num_cb: (optional) If a callback is specified with
678 the cb parameter this parameter determines the
679 granularity of the callback by defining
680 the maximum number of times the callback will
681 be called during the file transfer.
682
683 :type policy: :class:`boto.gs.acl.CannedACLStrings`
684 :param policy: A canned ACL policy that will be applied to the
685 new key in GCS.
686
687 :type md5: A tuple containing the hexdigest version of the MD5
688 checksum of the file as the first element and the
689 Base64-encoded version of the plain checksum as the
690 second element. This is the same format returned by
691 the compute_md5 method.
692 :param md5: If you need to compute the MD5 for any reason prior
693 to upload, it's silly to have to do it twice so this
694 param, if present, will be used as the MD5 values
695 of the file. Otherwise, the checksum will be computed.
696
697 :type if_generation: int
698 :param if_generation: (optional) If set to a generation number, the
699 object will only be written to if its current generation number is
700 this value. If set to the value 0, the object will only be written
701 if it doesn't already exist.
702 """
703
704 # Clear out any previously computed md5 hashes, since we are setting the content.
705 self.md5 = None
706 self.base64md5 = None
707
708 fp = StringIO(get_utf8_value(s))
709 r = self.set_contents_from_file(fp, headers, replace, cb, num_cb,
710 policy, md5,
711 if_generation=if_generation)
712 fp.close()
713 return r
714
715 def set_contents_from_stream(self, *args, **kwargs):
716 """
717 Store an object using the name of the Key object as the key in
718 cloud and the contents of the data stream pointed to by 'fp' as
719 the contents.
720
721 The stream object is not seekable and total size is not known.
722 This has the implication that we can't specify the
723 Content-Size and Content-MD5 in the header. So for huge
724 uploads, the delay in calculating MD5 is avoided but with a
725 penalty of inability to verify the integrity of the uploaded
726 data.
727
728 :type fp: file
729 :param fp: the file whose contents are to be uploaded
730
731 :type headers: dict
732 :param headers: additional HTTP headers to be sent with the
733 PUT request.
734
735 :type replace: bool
736 :param replace: If this parameter is False, the method will first check
737 to see if an object exists in the bucket with the same key. If it
738 does, it won't overwrite it. The default value is True which will
739 overwrite the object.
740
741 :type cb: function
742 :param cb: a callback function that will be called to report
743 progress on the upload. The callback should accept two integer
744 parameters, the first representing the number of bytes that have
745 been successfully transmitted to GS and the second representing the
746 total number of bytes that need to be transmitted.
747
748 :type num_cb: int
749 :param num_cb: (optional) If a callback is specified with the
750 cb parameter, this parameter determines the granularity of
751 the callback by defining the maximum number of times the
752 callback will be called during the file transfer.
753
754 :type policy: :class:`boto.gs.acl.CannedACLStrings`
755 :param policy: A canned ACL policy that will be applied to the new key
756 in GS.
757
758 :type size: int
759 :param size: (optional) The Maximum number of bytes to read from
760 the file pointer (fp). This is useful when uploading a
761 file in multiple parts where you are splitting the file up
762 into different ranges to be uploaded. If not specified,
763 the default behaviour is to read all bytes from the file
764 pointer. Less bytes may be available.
765
766 :type if_generation: int
767 :param if_generation: (optional) If set to a generation number, the
768 object will only be written to if its current generation number is
769 this value. If set to the value 0, the object will only be written
770 if it doesn't already exist.
771 """
772 if_generation = kwargs.pop('if_generation', None)
773 if if_generation is not None:
774 headers = kwargs.get('headers', {})
775 headers['x-goog-if-generation-match'] = str(if_generation)
776 kwargs['headers'] = headers
777 super(Key, self).set_contents_from_stream(*args, **kwargs)
778
779 def set_acl(self, acl_or_str, headers=None, generation=None,
780 if_generation=None, if_metageneration=None):
781 """Sets the ACL for this object.
782
783 :type acl_or_str: string or :class:`boto.gs.acl.ACL`
784 :param acl_or_str: A canned ACL string (see
785 :data:`~.gs.acl.CannedACLStrings`) or an ACL object.
786
787 :type headers: dict
788 :param headers: Additional headers to set during the request.
789
790 :type generation: int
791 :param generation: If specified, sets the ACL for a specific generation
792 of a versioned object. If not specified, the current version is
793 modified.
794
795 :type if_generation: int
796 :param if_generation: (optional) If set to a generation number, the acl
797 will only be updated if its current generation number is this value.
798
799 :type if_metageneration: int
800 :param if_metageneration: (optional) If set to a metageneration number,
801 the acl will only be updated if its current metageneration number is
802 this value.
803 """
804 if self.bucket is not None:
805 self.bucket.set_acl(acl_or_str, self.name, headers=headers,
806 generation=generation,
807 if_generation=if_generation,
808 if_metageneration=if_metageneration)
809
810 def get_acl(self, headers=None, generation=None):
811 """Returns the ACL of this object.
812
813 :param dict headers: Additional headers to set during the request.
814
815 :param int generation: If specified, gets the ACL for a specific
816 generation of a versioned object. If not specified, the current
817 version is returned.
818
819 :rtype: :class:`.gs.acl.ACL`
820 """
821 if self.bucket is not None:
822 return self.bucket.get_acl(self.name, headers=headers,
823 generation=generation)
824
825 def get_xml_acl(self, headers=None, generation=None):
826 """Returns the ACL string of this object.
827
828 :param dict headers: Additional headers to set during the request.
829
830 :param int generation: If specified, gets the ACL for a specific
831 generation of a versioned object. If not specified, the current
832 version is returned.
833
834 :rtype: str
835 """
836 if self.bucket is not None:
837 return self.bucket.get_xml_acl(self.name, headers=headers,
838 generation=generation)
839
840 def set_xml_acl(self, acl_str, headers=None, generation=None,
841 if_generation=None, if_metageneration=None):
842 """Sets this objects's ACL to an XML string.
843
844 :type acl_str: string
845 :param acl_str: A string containing the ACL XML.
846
847 :type headers: dict
848 :param headers: Additional headers to set during the request.
849
850 :type generation: int
851 :param generation: If specified, sets the ACL for a specific generation
852 of a versioned object. If not specified, the current version is
853 modified.
854
855 :type if_generation: int
856 :param if_generation: (optional) If set to a generation number, the acl
857 will only be updated if its current generation number is this value.
858
859 :type if_metageneration: int
860 :param if_metageneration: (optional) If set to a metageneration number,
861 the acl will only be updated if its current metageneration number is
862 this value.
863 """
864 if self.bucket is not None:
865 return self.bucket.set_xml_acl(acl_str, self.name, headers=headers,
866 generation=generation,
867 if_generation=if_generation,
868 if_metageneration=if_metageneration)
869
870 def set_canned_acl(self, acl_str, headers=None, generation=None,
871 if_generation=None, if_metageneration=None):
872 """Sets this objects's ACL using a predefined (canned) value.
873
874 :type acl_str: string
875 :param acl_str: A canned ACL string. See
876 :data:`~.gs.acl.CannedACLStrings`.
877
878 :type headers: dict
879 :param headers: Additional headers to set during the request.
880
881 :type generation: int
882 :param generation: If specified, sets the ACL for a specific generation
883 of a versioned object. If not specified, the current version is
884 modified.
885
886 :type if_generation: int
887 :param if_generation: (optional) If set to a generation number, the acl
888 will only be updated if its current generation number is this value.
889
890 :type if_metageneration: int
891 :param if_metageneration: (optional) If set to a metageneration number,
892 the acl will only be updated if its current metageneration number is
893 this value.
894 """
895 if self.bucket is not None:
896 return self.bucket.set_canned_acl(
897 acl_str,
898 self.name,
899 headers=headers,
900 generation=generation,
901 if_generation=if_generation,
902 if_metageneration=if_metageneration
903 )
904
905 def compose(self, components, content_type=None, headers=None):
906 """Create a new object from a sequence of existing objects.
907
908 The content of the object representing this Key will be the
909 concatenation of the given object sequence. For more detail, visit
910
911 https://developers.google.com/storage/docs/composite-objects
912
913 :type components list of Keys
914 :param components List of gs.Keys representing the component objects
915
916 :type content_type (optional) string
917 :param content_type Content type for the new composite object.
918 """
919 compose_req = []
920 for key in components:
921 if key.bucket.name != self.bucket.name:
922 raise BotoClientError(
923 'GCS does not support inter-bucket composing')
924
925 generation_tag = ''
926 if key.generation:
927 generation_tag = ('<Generation>%s</Generation>'
928 % str(key.generation))
929 compose_req.append('<Component><Name>%s</Name>%s</Component>' %
930 (key.name, generation_tag))
931 compose_req_xml = ('<ComposeRequest>%s</ComposeRequest>' %
932 ''.join(compose_req))
933 headers = headers or {}
934 if content_type:
935 headers['Content-Type'] = content_type
936 resp = self.bucket.connection.make_request(
937 'PUT', get_utf8_value(self.bucket.name), get_utf8_value(self.name),
938 headers=headers, query_args='compose',
939 data=get_utf8_value(compose_req_xml))
940 if resp.status < 200 or resp.status > 299:
941 raise self.bucket.connection.provider.storage_response_error(
942 resp.status, resp.reason, resp.read())
943
944 # Return the generation so that the result URI can be built with this
945 # for automatic parallel uploads.
946 return resp.getheader('x-goog-generation')