def __SendMediaBody(self, start, additional_headers=None): """Send the entire media stream in a single request.""" self.EnsureInitialized() if self.total_size is None: raise exceptions.TransferInvalidError( 'Total size must be known for SendMediaBody') body_stream = stream_slice.StreamSlice(self.stream, self.total_size - start) request = http_wrapper.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 if additional_headers: request.headers.update(additional_headers) return self.__SendMediaRequest(request, self.total_size)
def __NormalizeStartEnd(self, start, end=None): if end is not None: if start < 0: raise exceptions.TransferInvalidError( 'Cannot have end index with negative start index') elif start >= self.total_size: raise exceptions.TransferInvalidError( 'Cannot have start index greater than total size') end = min(end, self.total_size - 1) if end < start: raise exceptions.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
def read(self, size=None): if size is not None: size = min(size, self.__remaining_bytes) else: size = self.__remaining_bytes data = self.__stream.read(size) if not data and self.__remaining_bytes: raise exceptions.TransferInvalidError( 'Not enough bytes in stream; expected %d, stream exhasted after %d' % (self.__max_bytes, self.__remaining_bytes)) self.__remaining_bytes -= len(data) return data
def __StreamMedia(self, callback=None, finish_callback=None, additional_headers=None, use_chunks=True): """Helper function for StreamMedia / StreamInChunks.""" if self.strategy != RESUMABLE_UPLOAD: raise exceptions.InvalidUserInputError( 'Cannot stream non-resumable upload') callback = callback or self._ArgPrinter finish_callback = finish_callback or self._CompletePrinter # final_response is set if we resumed an already-completed upload. response = self.__final_response send_func = self.__SendChunk if use_chunks else self.__SendMediaBody if use_chunks: self.__ValidateChunksize(self.chunksize) self.EnsureInitialized() while not self.complete: response = send_func(self.stream.tell(), additional_headers=additional_headers) if response.status_code in (httplib.OK, httplib.CREATED): self.__complete = True break self.__progress = self.__GetLastByte(response.info['range']) if self.progress + 1 != self.stream.tell(): # TODO: Add a better way to recover here. raise exceptions.CommunicationError( 'Failed to transfer all bytes in chunk, upload paused at byte ' '%d' % self.progress) self._ExecuteCallback(callback, response) if self.__complete: # TODO: Decide how to handle errors in the non-seekable case. 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 exceptions.TransferInvalidError( 'Upload complete with %s additional bytes left in stream' % (long(end_pos) - long(current_pos))) self._ExecuteCallback(finish_callback, response) return response
def EnsureUninitialized(self): if self.initialized: raise exceptions.TransferInvalidError('Cannot re-initialize %s', self._type_name)
def EnsureInitialized(self): if not self.initialized: raise exceptions.TransferInvalidError( 'Cannot use uninitialized %s', self._type_name)