예제 #1
0
    def _normalize_start_end(self, start, end=None):
        """Validate / fix up byte range.

        :type start: integer
        :param start: start byte of the range:  if negative, used as an
                      offset from the end.

        :type end: integer
        :param end: end byte of the range.

        :rtype: tuple, (start, end)
        :returns:  the normalized start, end pair.
        :raises: :exc:`gcloud.streaming.exceptions.TransferInvalidError`
                 for invalid combinations of start, end.
        """
        if end is not None:
            if start < 0:
                raise TransferInvalidError(
                    'Cannot have end index with negative start index')
            elif start >= self.total_size:
                raise TransferInvalidError(
                    'Cannot have start index greater than total size')
            end = min(end, self.total_size - 1)
            if end < start:
                raise TransferInvalidError(
                    'Range requested with end[%s] < start[%s]' % (end, start))
            return start, end
        else:
            if start < 0:
                start = max(0, start + self.total_size)
            return start, self.total_size - 1
예제 #2
0
    def _send_media_body(self, start):
        """ Send the entire stream in a single request.

        Helper for :meth:`stream_file`:

        :type start: integer
        :param start: start byte of the range.
        """
        self._ensure_initialized()
        if self.total_size is None:
            raise TransferInvalidError(
                'Total size must be known for SendMediaBody')
        body_stream = StreamSlice(self.stream, self.total_size - start)

        request = Request(url=self.url, http_method='PUT', body=body_stream)
        request.headers['Content-Type'] = self.mime_type
        if start == self.total_size:
            # End of an upload with 0 bytes left to send; just finalize.
            range_string = 'bytes */%s' % self.total_size
        else:
            range_string = 'bytes %s-%s/%s' % (start, self.total_size - 1,
                                               self.total_size)

        request.headers['Content-Range'] = range_string

        return self._send_media_request(request, self.total_size)
예제 #3
0
    def stream_file(self, use_chunks=True):
        """Upload the stream.

        :type use_chunks: boolean
        :param use_chunks: If False, send the stream in a single request.
                          Otherwise, send it in chunks.
        """
        if self.strategy != RESUMABLE_UPLOAD:
            raise ValueError('Cannot stream non-resumable upload')
        # final_response is set if we resumed an already-completed upload.
        response = self._final_response
        send_func = self._send_chunk if use_chunks else self._send_media_body
        if use_chunks:
            self._validate_chunksize(self.chunksize)
        self._ensure_initialized()
        while not self.complete:
            response = send_func(self.stream.tell())
            if response.status_code in (http_client.OK, http_client.CREATED):
                self._complete = True
                break
            self._progress = self._last_byte(response.info['range'])
            if self.progress + 1 != self.stream.tell():
                raise CommunicationError(
                    'Failed to transfer all bytes in chunk, upload paused at '
                    'byte %d' % self.progress)
        if self.complete and hasattr(self.stream, 'seek'):
            current_pos = self.stream.tell()
            self.stream.seek(0, os.SEEK_END)
            end_pos = self.stream.tell()
            self.stream.seek(current_pos)
            if current_pos != end_pos:
                raise TransferInvalidError(
                    'Upload complete with %s additional bytes left in stream' %
                    (int(end_pos) - int(current_pos)))
        return response
예제 #4
0
    def _ensure_uninitialized(self):
        """Helper:  assert that the instance is not initialized.

        :raises: :exc:`gcloud.streaming.exceptions.TransferInvalidError`
                 if the instance is already initialized.
        """
        if self.initialized:
            raise TransferInvalidError('Cannot re-initialize %s',
                                       type(self).__name__)