def store_and_build_forward_message(self, form, boundary=None,
                                      max_bytes_per_blob=None,
                                      max_bytes_total=None,
                                      bucket_name=None):
    """Reads form data, stores blobs data and builds the forward request.

    This finds all of the file uploads in a set of form fields, converting them
    into blobs and storing them in the blobstore. It also generates the HTTP
    request to forward to the user's application.

    Args:
      form: cgi.FieldStorage instance representing the whole form derived from
        original POST data.
      boundary: The optional boundary to use for the resulting form. If omitted,
        one is randomly generated.
      max_bytes_per_blob: The maximum size in bytes that any single blob
        in the form is allowed to be.
      max_bytes_total: The maximum size in bytes that the total of all blobs
        in the form is allowed to be.
      bucket_name: The name of the Google Storage bucket to store the uploaded
                   files.

    Returns:
      A tuple (content_type, content_text), where content_type is the value of
      the Content-Type header, and content_text is a string containing the body
      of the HTTP request to forward to the application.

    Raises:
      webob.exc.HTTPException: The upload failed.
    """
    message = multipart.MIMEMultipart('form-data', boundary)

    creation = self._now_func()
    total_bytes_uploaded = 0
    created_blobs = []
    mime_type_error = None
    too_many_conflicts = False
    upload_too_large = False
    filename_too_large = False
    content_type_too_large = False

    # Extract all of the individual form items out of the FieldStorage.
    form_items = []
    # Sorting of forms is done merely to make testing a little easier since
    # it means blob-keys are generated in a predictable order.
    for key in sorted(form):
      form_item = form[key]
      if isinstance(form_item, list):
        form_items.extend(form_item)
      else:
        form_items.append(form_item)

    for form_item in form_items:
      disposition_parameters = {'name': form_item.name}

      variable = email.message.Message()

      if form_item.filename is None:
        # Copy as is
        variable.add_header('Content-Type', 'text/plain')
        variable.set_payload(form_item.value)
      else:
        # If there is no filename associated with this field it means that the
        # file form field was not filled in.  This blob should not be created
        # and forwarded to success handler.
        if not form_item.filename:
          continue

        disposition_parameters['filename'] = form_item.filename

        try:
          main_type, sub_type = _split_mime_type(form_item.type)
        except _InvalidMIMETypeFormatError, ex:
          mime_type_error = str(ex)
          break

        # Seek to the end of file and use the pos as the length.
        form_item.file.seek(0, os.SEEK_END)
        content_length = form_item.file.tell()
        form_item.file.seek(0)

        total_bytes_uploaded += content_length

        if max_bytes_per_blob is not None:
          if content_length > max_bytes_per_blob:
            upload_too_large = True
            break
        if max_bytes_total is not None:
          if total_bytes_uploaded > max_bytes_total:
            upload_too_large = True
            break
        if form_item.filename is not None:
          if len(form_item.filename) > _MAX_STRING_NAME_LENGTH:
            filename_too_large = True
            break
        if form_item.type is not None:
          if len(form_item.type) > _MAX_STRING_NAME_LENGTH:
            content_type_too_large = True
            break

        # Compute the MD5 hash of the upload.
        digester = hashlib.md5()
        while True:
          block = form_item.file.read(1 << 20)
          if not block:
            break
          digester.update(block)
        form_item.file.seek(0)

        # Create the external body message containing meta-data about the blob.
        external = email.message.Message()
        external.add_header('Content-Type', '%s/%s' % (main_type, sub_type),
                            **form_item.type_options)
        # NOTE: This is in violation of RFC 2616 (Content-MD5 should be the
        # base-64 encoding of the binary hash, not the hex digest), but it is
        # consistent with production.
        content_md5 = base64.urlsafe_b64encode(digester.hexdigest())
        # Create header MIME message
        headers = dict(form_item.headers)
        for name in _STRIPPED_FILE_HEADERS:
          if name in headers:
            del headers[name]
        headers['Content-Length'] = str(content_length)
        headers[blobstore.UPLOAD_INFO_CREATION_HEADER] = (
            blobstore._format_creation(creation))
        headers['Content-MD5'] = content_md5
        gs_filename = None
        if bucket_name:
          random_key = str(self._generate_blob_key())
          gs_filename = '%s/fake-%s' % (bucket_name, random_key)
          headers[blobstore.CLOUD_STORAGE_OBJECT_HEADER] = (
              blobstore.GS_PREFIX + gs_filename)
        for key, value in headers.iteritems():
          external.add_header(key, value)
        # Add disposition parameters (a clone of the outer message's field).
        if not external.get('Content-Disposition'):
          external.add_header('Content-Disposition', 'form-data',
                              **disposition_parameters)

        base64_encoding = (form_item.headers.get('Content-Transfer-Encoding') ==
                           'base64')
        content_type, blob_file, filename = self._preprocess_data(
            external['content-type'],
            form_item.file,
            form_item.filename,
            base64_encoding)

        # Store the actual contents to storage.
        if gs_filename:
          info_entity = self.store_gs_file(
              content_type, gs_filename, blob_file, filename)
        else:
          try:
            info_entity = self.store_blob(content_type, filename,
                                          digester, blob_file, creation)
          except _TooManyConflictsError:
            too_many_conflicts = True
            break

        # Track created blobs in case we need to roll them back.
        created_blobs.append(info_entity)

        variable.add_header('Content-Type', 'message/external-body',
                            access_type=blobstore.BLOB_KEY_HEADER,
                            blob_key=info_entity.key().name())
        variable.set_payload([external])

      # Set common information.
      variable.add_header('Content-Disposition', 'form-data',
                          **disposition_parameters)
      message.attach(variable)

    if (mime_type_error or too_many_conflicts or upload_too_large or
        filename_too_large or content_type_too_large):
      for blob in created_blobs:
        datastore.Delete(blob)
      if mime_type_error:
        self.abort(400, detail=mime_type_error)
      elif too_many_conflicts:
        self.abort(500, detail='Could not generate a blob key.')
      elif upload_too_large:
        self.abort(413)
      else:
        if filename_too_large:
          invalid_field = 'filename'
        elif content_type_too_large:
          invalid_field = 'Content-Type'
        detail = 'The %s exceeds the maximum allowed length of %s.' % (
            invalid_field, _MAX_STRING_NAME_LENGTH)
        self.abort(400, detail=detail)

    message_out = cStringIO.StringIO()
    gen = email.generator.Generator(message_out, maxheaderlen=0)
    gen.flatten(message, unixfrom=False)

    # Get the content text out of the message.
    message_text = message_out.getvalue()
    content_start = message_text.find('\n\n') + 2
    content_text = message_text[content_start:]
    content_text = content_text.replace('\n', '\r\n')

    return message.get('Content-Type'), content_text
dataset/ETHPy150Open AppScale/appscale/AppServer/google/appengine/tools/devappserver2/blob_upload.py/Application.store_and_build_forward_message/original