github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/mime/multipart/formdata.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package multipart 6 7 import ( 8 "bytes" 9 "errors" 10 "io" 11 "io/ioutil" 12 "net/textproto" 13 "os" 14 ) 15 16 // TODO(adg,bradfitz): find a way to unify the DoS-prevention strategy here 17 // with that of the http package's ParseForm. 18 19 // ReadForm parses an entire multipart message whose parts have 20 // a Content-Disposition of "form-data". 21 // It stores up to maxMemory bytes of the file parts in memory 22 // and the remainder on disk in temporary files. 23 func (r *Reader) ReadForm(maxMemory int64) (*Form, error) { 24 return r.readForm(maxMemory) 25 } 26 27 func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { 28 form := &Form{make(map[string][]string), make(map[string][]*FileHeader)} 29 defer func() { 30 if err != nil { 31 form.RemoveAll() 32 } 33 }() 34 35 maxValueBytes := int64(10 << 20) // 10 MB is a lot of text. 36 for { 37 p, err := r.NextPart() 38 if err == io.EOF { 39 break 40 } 41 if err != nil { 42 return nil, err 43 } 44 45 name := p.FormName() 46 if name == "" { 47 continue 48 } 49 filename := p.FileName() 50 51 var b bytes.Buffer 52 53 if filename == "" { 54 // value, store as string in memory 55 n, err := io.CopyN(&b, p, maxValueBytes) 56 if err != nil && err != io.EOF { 57 return nil, err 58 } 59 maxValueBytes -= n 60 if maxValueBytes == 0 { 61 return nil, errors.New("multipart: message too large") 62 } 63 form.Value[name] = append(form.Value[name], b.String()) 64 continue 65 } 66 67 // file, store in memory or on disk 68 fh := &FileHeader{ 69 Filename: filename, 70 Header: p.Header, 71 } 72 n, err := io.CopyN(&b, p, maxMemory+1) 73 if err != nil && err != io.EOF { 74 return nil, err 75 } 76 if n > maxMemory { 77 // too big, write to disk and flush buffer 78 file, err := ioutil.TempFile("", "multipart-") 79 if err != nil { 80 return nil, err 81 } 82 size, err := io.Copy(file, io.MultiReader(&b, p)) 83 if cerr := file.Close(); err == nil { 84 err = cerr 85 } 86 if err != nil { 87 os.Remove(file.Name()) 88 return nil, err 89 } 90 fh.tmpfile = file.Name() 91 fh.Size = size 92 } else { 93 fh.content = b.Bytes() 94 fh.Size = int64(len(fh.content)) 95 maxMemory -= n 96 } 97 form.File[name] = append(form.File[name], fh) 98 } 99 100 return form, nil 101 } 102 103 // Form is a parsed multipart form. 104 // Its File parts are stored either in memory or on disk, 105 // and are accessible via the *FileHeader's Open method. 106 // Its Value parts are stored as strings. 107 // Both are keyed by field name. 108 type Form struct { 109 Value map[string][]string 110 File map[string][]*FileHeader 111 } 112 113 // RemoveAll removes any temporary files associated with a Form. 114 func (f *Form) RemoveAll() error { 115 var err error 116 for _, fhs := range f.File { 117 for _, fh := range fhs { 118 if fh.tmpfile != "" { 119 e := os.Remove(fh.tmpfile) 120 if e != nil && err == nil { 121 err = e 122 } 123 } 124 } 125 } 126 return err 127 } 128 129 // A FileHeader describes a file part of a multipart request. 130 type FileHeader struct { 131 Filename string 132 Header textproto.MIMEHeader 133 Size int64 134 135 content []byte 136 tmpfile string 137 } 138 139 // Open opens and returns the FileHeader's associated File. 140 func (fh *FileHeader) Open() (File, error) { 141 if b := fh.content; b != nil { 142 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) 143 return sectionReadCloser{r}, nil 144 } 145 return os.Open(fh.tmpfile) 146 } 147 148 // File is an interface to access the file part of a multipart message. 149 // Its contents may be either stored in memory or on disk. 150 // If stored on disk, the File's underlying concrete type will be an *os.File. 151 type File interface { 152 io.Reader 153 io.ReaderAt 154 io.Seeker 155 io.Closer 156 } 157 158 // helper types to turn a []byte into a File 159 160 type sectionReadCloser struct { 161 *io.SectionReader 162 } 163 164 func (rc sectionReadCloser) Close() error { 165 return nil 166 }