github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/appengine/blobstore_mgt/my-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 23 24 // ParseUpload parses the synthetic POST request that your app gets from 25 // App Engine after a user's successful upload of blobs. 26 func MyParseUpload(req *http.Request) (blobs map[string][]*BlobInfo, 27 other url.Values, err error) { 28 29 _, params, errCT := mime.ParseMediaType(req.Header.Get("Content-Type")) 30 if errCT != nil { 31 return nil, nil, errCT 32 } 33 34 boundary := params["boundary"] 35 if boundary == "" { 36 return nil, nil, fmt.Errorf("did not find MIME multipart boundary") 37 } 38 39 blobs = make(map[string][]*BlobInfo) 40 other = make(url.Values) 41 42 mreader := multipart.NewReader(io.MultiReader(req.Body, strings.NewReader("\r\n\r\n")), boundary) 43 cntr := 0 44 for { 45 part, perr := mreader.NextPart() 46 if perr == io.EOF { 47 break 48 } 49 if perr != nil { 50 return nil, nil, errorf("error reading next mime part with boundary %q (len=%d): %v", 51 boundary, len(boundary), perr) 52 } 53 54 bi := &BlobInfo{} 55 ctype, params, err := mime.ParseMediaType(part.Header.Get("Content-Disposition")) 56 if err != nil { 57 return nil, nil, err 58 } 59 bi.Filename = params["filename"] 60 pFile := params["post_field_file"] // WRONG 61 pFile = params["name"] 62 63 ctype, params, err = mime.ParseMediaType(part.Header.Get("Content-Type")) 64 if err != nil { 65 return nil, nil, err 66 } 67 68 69 bi.BlobKey = appengine.BlobKey(params["blob-key"]) 70 if ctype != "message/external-body" || bi.BlobKey == "" { 71 if pFile != "" { 72 slurp, serr := ioutil.ReadAll(part) 73 if serr != nil { 74 return nil, nil, errorf("error reading %q MIME part", pFile) 75 } 76 other[pFile] = append(other[pFile], string(slurp)) 77 } 78 continue 79 } 80 81 82 // App Engine sends a MIME header as the body of each MIME part. 83 tp := textproto.NewReader(bufio.NewReader(part)) 84 85 86 header, mimeerr := tp.ReadMIMEHeader() 87 if mimeerr != nil { 88 s := mimeerr.Error() 89 if strings.HasPrefix(s, "malformed MIME header") { 90 return nil, nil, fmt.Errorf("'malformed' %q", mimeerr) 91 } else { 92 return nil, nil, fmt.Errorf("error reading again %q", mimeerr) 93 } 94 } 95 96 bi.Size, err = strconv.ParseInt(header.Get("Content-Length"), 10, 64) 97 if err != nil { 98 return nil, nil, err 99 } 100 bi.ContentType = header.Get("Content-Type") 101 102 // Parse the time from the MIME header like: 103 // X-AppEngine-Upload-Creation: 2011-03-15 21:38:34.712136 104 createDate := header.Get("X-AppEngine-Upload-Creation") 105 if createDate == "" { 106 return nil, nil, fmt.Errorf("expected to find an X-AppEngine-Upload-Creation header") 107 } 108 bi.CreationTime, err = time.Parse("2006-01-02 15:04:05.000000", createDate) 109 if err != nil { 110 return nil, nil, fmt.Errorf("error parsing X-AppEngine-Upload-Creation: %s", err) 111 } 112 113 if hdr := header.Get("Content-MD5"); hdr != "" { 114 md5, err := base64.URLEncoding.DecodeString(hdr) 115 if err != nil { 116 return nil, nil, fmt.Errorf("bad Content-MD5 %q: %v", hdr, err) 117 } 118 bi.MD5 = string(md5) 119 } 120 121 122 if pFile != "" { 123 // slurp, serr := ioutil.ReadAll(part) 124 // if serr != nil { 125 // return nil, nil, fmt.Errorf("error reading all from %q", pFile) 126 // } 127 // other[pFile] = append(other[pFile], string(slurp)) 128 } 129 130 blobs[pFile] = append(blobs[pFile], bi) 131 cntr++ 132 } 133 return 134 } 135 136 137 138