github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/appengine/blobstore_mgt/official-multipart-parse.go (about)

     1  package blobstore_mgt
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"mime"
    10  	"mime/multipart"
    11  	"net/http"
    12  	"net/textproto"
    13  
    14  	"net/url"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	"appengine"
    20  )
    21  
    22  func errorf(format string, args ...interface{}) error {
    23  	return fmt.Errorf("blobstore: "+format, args...)
    24  }
    25  
    26  
    27  
    28  // ParseUpload parses the synthetic POST request that your app gets from
    29  // App Engine after a user's successful upload of blobs. Given the request,
    30  // ParseUpload returns a map of the blobs received (keyed by HTML form
    31  // element name) and other non-blob POST parameters.
    32  func OfficialParseUpload(req *http.Request) (blobs map[string][]*BlobInfo, other url.Values, err error) {
    33  	_, params, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
    34  	if err != nil {
    35  		return nil, nil, err
    36  	}
    37  	boundary := params["boundary"]
    38  	if boundary == "" {
    39  		return nil, nil, errorf("did not find MIME multipart boundary")
    40  	}
    41  
    42  	blobs = make(map[string][]*BlobInfo)
    43  	other = make(url.Values)
    44  
    45  	mreader := multipart.NewReader(io.MultiReader(req.Body, strings.NewReader("\r\n\r\n")), boundary)
    46  	for {
    47  		part, perr := mreader.NextPart()
    48  		if perr == io.EOF {
    49  			break
    50  		}
    51  		if perr != nil {
    52  			return nil, nil, errorf("error reading next mime part with boundary %q (len=%d): %v",
    53  				boundary, len(boundary), perr)
    54  		}
    55  
    56  		bi := &BlobInfo{}
    57  		ctype, params, err := mime.ParseMediaType(part.Header.Get("Content-Disposition"))
    58  		if err != nil {
    59  			return nil, nil, err
    60  		}
    61  		bi.Filename = params["filename"]
    62  		formKey := params["name"]
    63  
    64  		ctype, params, err = mime.ParseMediaType(part.Header.Get("Content-Type"))
    65  		if err != nil {
    66  			return nil, nil, err
    67  		}
    68  		bi.BlobKey = appengine.BlobKey(params["blob-key"])
    69  		if ctype != "message/external-body" || bi.BlobKey == "" {
    70  			if formKey != "" {
    71  				slurp, serr := ioutil.ReadAll(part)
    72  				if serr != nil {
    73  					return nil, nil, errorf("error reading %q MIME part", formKey)
    74  				}
    75  				other[formKey] = append(other[formKey], string(slurp))
    76  			}
    77  			continue
    78  		}
    79  
    80  		// App Engine sends a MIME header as the body of each MIME part.
    81  		tp := textproto.NewReader(bufio.NewReader(part))
    82  		header, mimeerr := tp.ReadMIMEHeader()
    83  		if mimeerr != nil {
    84  			return nil, nil, mimeerr
    85  		}
    86  		bi.Size, err = strconv.ParseInt(header.Get("Content-Length"), 10, 64)
    87  		if err != nil {
    88  			return nil, nil, err
    89  		}
    90  		bi.ContentType = header.Get("Content-Type")
    91  
    92  		// Parse the time from the MIME header like:
    93  		// X-AppEngine-Upload-Creation: 2011-03-15 21:38:34.712136
    94  		createDate := header.Get("X-AppEngine-Upload-Creation")
    95  		if createDate == "" {
    96  			return nil, nil, errorf("expected to find an X-AppEngine-Upload-Creation header")
    97  		}
    98  		bi.CreationTime, err = time.Parse("2006-01-02 15:04:05.000000", createDate)
    99  		if err != nil {
   100  			return nil, nil, errorf("error parsing X-AppEngine-Upload-Creation: %s", err)
   101  		}
   102  
   103  		if hdr := header.Get("Content-MD5"); hdr != "" {
   104  			md5, err := base64.URLEncoding.DecodeString(hdr)
   105  			if err != nil {
   106  				return nil, nil, errorf("bad Content-MD5 %q: %v", hdr, err)
   107  			}
   108  			bi.MD5 = string(md5)
   109  		}
   110  
   111  		// If the GCS object name was provided, record it.
   112  		bi.ObjectName = header.Get("X-AppEngine-Cloud-Storage-Object")
   113  
   114  		blobs[formKey] = append(blobs[formKey], bi)
   115  	}
   116  	return
   117  }
   118  
   119  
   120  
   121