'Store an object in GS using the name of the Key object as the
key in GS and the contents of the file pointed to by \'fp\' as the
contents.
:type fp: file
:param fp: The file whose contents are to be uploaded.
:type headers: dict
:param headers: (optional) Additional HTTP headers to be sent with the
PUT request.
:type replace: bool
:param replace: (optional) 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: (optional) Callback function that will be called to report
progress on the upload. The callback should accept two integer
parameters, the first representing the number of bytes that have
been successfully transmitted to GS and the second representing the
total number of bytes that need to be transmitted.
:type num_cb: int
:param num_cb: (optional) If a callback is specified with the cb
parameter, this parameter determines the granularity of the callback
by defining the maximum number of times the callback will be called
during the file transfer.
:type policy: :class:`boto.gs.acl.CannedACLStrings`
:param policy: (optional) A canned ACL policy that will be applied to
the new key in GS.
:type md5: tuple
:param md5: (optional) 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.
If you need to compute the MD5 for any reason prior to upload, it\'s
silly to have to do it twice so this param, if present, will be
used as the MD5 values of the file. Otherwise, the checksum will be
computed.
:type res_upload_handler: :py:class:`boto.gs.resumable_upload_handler.ResumableUploadHandler`
:param res_upload_handler: (optional) If provided, this handler will
perform the upload.
:type size: int
:param size: (optional) The Maximum number of bytes to read from the
file pointer (fp). This is useful when uploading a file in multiple
parts where you are splitting the file up into different ranges to
be uploaded. If not specified, the default behaviour is to read all
bytes from the file pointer. Less bytes may be available.
Notes:
1. The "size" parameter currently cannot be used when a
resumable upload handler is given but is still useful for
uploading part of a file as implemented by the parent class.
2. At present Google Cloud Storage does not support multipart
uploads.
:type rewind: bool
:param rewind: (optional) If True, the file pointer (fp) will be
rewound to the start before any bytes are read from it. The default
behaviour is False which reads from the current position of the
file pointer (fp).
:type if_generation: int
:param if_generation: (optional) If set to a generation number, the
object will only be written to if its current generation number is
this value. If set to the value 0, the object will only be written
if it doesn\'t already exist.
:rtype: int
:return: The number of bytes written to the key.
TODO: At some point we should refactor the Bucket and Key classes,
to move functionality common to all providers into a parent class,
and provider-specific functionality into subclasses (rather than
just overriding/sharing code the way it currently works).'

def set_contents_from_file(self, fp, headers=None, replace=True, cb=None, num_cb=10, policy=None, md5=None, res_upload_handler=None, size=None, rewind=False, if_generation=None):

    provider = self.bucket.connection.provider
    if (res_upload_handler and size):
        raise BotoClientError('"size"    param    not    supported    for    resumable    uploads.')
    headers = (headers or {})
    if policy:
        headers[provider.acl_header] = policy
    if rewind:
        fp.seek(0, os.SEEK_SET)
    elif (not isinstance(fp, KeyFile)):
        spos = fp.tell()
        fp.seek(0, os.SEEK_END)
        if (fp.tell() == spos):
            fp.seek(0, os.SEEK_SET)
            if (fp.tell() != spos):
                fp.seek(spos)
                raise AttributeError('fp    is    at    EOF.    Use    rewind    option    or    seek()    to    data    start.')
        fp.seek(spos)
    if hasattr(fp, 'name'):
        self.path = fp.name
    if (self.bucket is not None):
        if isinstance(fp, KeyFile):
            key = fp.getkey()
            size = (key.size - fp.tell())
            self.size = size
            if re.match('^"[a-fA-F0-9]{32}"$', key.etag):
                etag = key.etag.strip('"')
                md5 = (etag, base64.b64encode(binascii.unhexlify(etag)))
        if size:
            self.size = size
        else:
            spos = fp.tell()
            fp.seek(0, os.SEEK_END)
            self.size = (fp.tell() - spos)
            fp.seek(spos)
            size = self.size
        if (md5 is None):
            md5 = self.compute_md5(fp, size)
        self.md5 = md5[0]
        self.base64md5 = md5[1]
        if (self.name is None):
            self.name = self.md5
        if (not replace):
            if self.bucket.lookup(self.name):
                return
        if (if_generation is not None):
            headers['x-goog-if-generation-match'] = str(if_generation)
        if res_upload_handler:
            res_upload_handler.send_file(self, fp, headers, cb, num_cb)
        else:
            self.send_file(fp, headers, cb, num_cb, size=size)
