| # Copyright 2010 Google Inc. |
| # Copyright (c) 2011, Nexenta Systems Inc. |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining a |
| # copy of this software and associated documentation files (the |
| # "Software"), to deal in the Software without restriction, including |
| # without limitation the rights to use, copy, modify, merge, publish, dis- |
| # tribute, sublicense, and/or sell copies of the Software, and to permit |
| # persons to whom the Software is furnished to do so, subject to the fol- |
| # lowing conditions: |
| # |
| # The above copyright notice and this permission notice shall be included |
| # in all copies or substantial portions of the Software. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| # IN THE SOFTWARE. |
| |
| # File representation of key, for use with "file://" URIs. |
| |
| import os, shutil, StringIO |
| import sys |
| |
| class Key(object): |
| |
| KEY_STREAM_READABLE = 0x01 |
| KEY_STREAM_WRITABLE = 0x02 |
| KEY_STREAM = (KEY_STREAM_READABLE | KEY_STREAM_WRITABLE) |
| KEY_REGULAR_FILE = 0x00 |
| |
| def __init__(self, bucket, name, fp=None, key_type=KEY_REGULAR_FILE): |
| self.bucket = bucket |
| self.full_path = name |
| if name == '-': |
| self.name = None |
| self.size = None |
| else: |
| self.name = name |
| self.size = os.stat(name).st_size |
| self.key_type = key_type |
| if key_type == self.KEY_STREAM_READABLE: |
| self.fp = sys.stdin |
| self.full_path = '<STDIN>' |
| elif key_type == self.KEY_STREAM_WRITABLE: |
| self.fp = sys.stdout |
| self.full_path = '<STDOUT>' |
| else: |
| self.fp = fp |
| |
| def __str__(self): |
| return 'file://' + self.full_path |
| |
| def get_file(self, fp, headers=None, cb=None, num_cb=10, torrent=False): |
| """ |
| Retrieves a file from a Key |
| |
| :type fp: file |
| :param fp: File pointer to put the data into |
| |
| :type headers: string |
| :param: ignored in this subclass. |
| |
| :type cb: function |
| :param cb: ignored in this subclass. |
| |
| :type cb: int |
| :param num_cb: ignored in this subclass. |
| """ |
| if self.key_type & self.KEY_STREAM_WRITABLE: |
| raise BotoClientError('Stream is not readable') |
| elif self.key_type & self.KEY_STREAM_READABLE: |
| key_file = self.fp |
| else: |
| key_file = open(self.full_path, 'rb') |
| try: |
| shutil.copyfileobj(key_file, fp) |
| finally: |
| key_file.close() |
| |
| def set_contents_from_file(self, fp, headers=None, replace=True, cb=None, |
| num_cb=10, policy=None, md5=None): |
| """ |
| Store an object in a file using the name of the Key object as the |
| key in file URI and the contents of the file pointed to by 'fp' as the |
| contents. |
| |
| :type fp: file |
| :param fp: the file whose contents to upload |
| |
| :type headers: dict |
| :param headers: ignored in this subclass. |
| |
| :type replace: bool |
| :param replace: If this parameter is False, the method |
| will first check to see if an object exists in the |
| bucket with the same key. If it does, it won't |
| overwrite it. The default value is True which will |
| overwrite the object. |
| |
| :type cb: function |
| :param cb: ignored in this subclass. |
| |
| :type cb: int |
| :param num_cb: ignored in this subclass. |
| |
| :type policy: :class:`boto.s3.acl.CannedACLStrings` |
| :param policy: ignored in this subclass. |
| |
| :type md5: A tuple containing the hexdigest version of the MD5 checksum |
| of the file as the first element and the Base64-encoded |
| version of the plain checksum as the second element. |
| This is the same format returned by the compute_md5 method. |
| :param md5: ignored in this subclass. |
| """ |
| if self.key_type & self.KEY_STREAM_READABLE: |
| raise BotoClientError('Stream is not writable') |
| elif self.key_type & self.KEY_STREAM_WRITABLE: |
| key_file = self.fp |
| else: |
| if not replace and os.path.exists(self.full_path): |
| return |
| key_file = open(self.full_path, 'wb') |
| try: |
| shutil.copyfileobj(fp, key_file) |
| finally: |
| key_file.close() |
| |
| def get_contents_to_file(self, fp, headers=None, cb=None, num_cb=None, |
| torrent=False, version_id=None, |
| res_download_handler=None, response_headers=None): |
| """ |
| Copy contents from the current file to the file pointed to by 'fp'. |
| |
| :type fp: File-like object |
| :param fp: |
| |
| :type headers: dict |
| :param headers: Unused in this subclass. |
| |
| :type cb: function |
| :param cb: Unused in this subclass. |
| |
| :type cb: int |
| :param num_cb: Unused in this subclass. |
| |
| :type torrent: bool |
| :param torrent: Unused in this subclass. |
| |
| :type res_upload_handler: ResumableDownloadHandler |
| :param res_download_handler: Unused in this subclass. |
| |
| :type response_headers: dict |
| :param response_headers: Unused in this subclass. |
| """ |
| shutil.copyfileobj(self.fp, fp) |
| |
| def get_contents_as_string(self, headers=None, cb=None, num_cb=10, |
| torrent=False): |
| """ |
| Retrieve file data from the Key, and return contents as a string. |
| |
| :type headers: dict |
| :param headers: ignored in this subclass. |
| |
| :type cb: function |
| :param cb: ignored in this subclass. |
| |
| :type cb: int |
| :param num_cb: ignored in this subclass. |
| |
| :type cb: int |
| :param num_cb: ignored in this subclass. |
| |
| :type torrent: bool |
| :param torrent: ignored in this subclass. |
| |
| :rtype: string |
| :returns: The contents of the file as a string |
| """ |
| |
| fp = StringIO.StringIO() |
| self.get_contents_to_file(fp) |
| return fp.getvalue() |
| |
| def is_stream(self): |
| return (self.key_type & self.KEY_STREAM) |
| |
| def close(self): |
| """ |
| Closes fp associated with underlying file. |
| Caller should call this method when done with this class, to avoid |
| using up OS resources (e.g., when iterating over a large number |
| of files). |
| """ |
| self.fp.close() |