...
1
2
3
4
5 package multipart
6
7 import (
8 "bytes"
9 "errors"
10 "io"
11 "io/ioutil"
12 "net/textproto"
13 "os"
14 )
15
16
17
18 var ErrMessageTooLarge = errors.New("multipart: message too large")
19
20
21
22
23
24
25
26
27
28
29
30 func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
31 return r.readForm(maxMemory)
32 }
33
34 func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
35 form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
36 defer func() {
37 if err != nil {
38 form.RemoveAll()
39 }
40 }()
41
42
43 maxValueBytes := maxMemory + int64(10<<20)
44 for {
45 p, err := r.NextPart()
46 if err == io.EOF {
47 break
48 }
49 if err != nil {
50 return nil, err
51 }
52
53 name := p.FormName()
54 if name == "" {
55 continue
56 }
57 filename := p.FileName()
58
59 var b bytes.Buffer
60
61 _, hasContentTypeHeader := p.Header["Content-Type"]
62 if !hasContentTypeHeader && filename == "" {
63
64 n, err := io.CopyN(&b, p, maxValueBytes+1)
65 if err != nil && err != io.EOF {
66 return nil, err
67 }
68 maxValueBytes -= n
69 if maxValueBytes < 0 {
70 return nil, ErrMessageTooLarge
71 }
72 form.Value[name] = append(form.Value[name], b.String())
73 continue
74 }
75
76
77 fh := &FileHeader{
78 Filename: filename,
79 Header: p.Header,
80 }
81 n, err := io.CopyN(&b, p, maxMemory+1)
82 if err != nil && err != io.EOF {
83 return nil, err
84 }
85 if n > maxMemory {
86
87 file, err := ioutil.TempFile("", "multipart-")
88 if err != nil {
89 return nil, err
90 }
91 size, err := io.Copy(file, io.MultiReader(&b, p))
92 if cerr := file.Close(); err == nil {
93 err = cerr
94 }
95 if err != nil {
96 os.Remove(file.Name())
97 return nil, err
98 }
99 fh.tmpfile = file.Name()
100 fh.Size = size
101 } else {
102 fh.content = b.Bytes()
103 fh.Size = int64(len(fh.content))
104 maxMemory -= n
105 maxValueBytes -= n
106 }
107 form.File[name] = append(form.File[name], fh)
108 }
109
110 return form, nil
111 }
112
113
114
115
116
117
118 type Form struct {
119 Value map[string][]string
120 File map[string][]*FileHeader
121 }
122
123
124 func (f *Form) RemoveAll() error {
125 var err error
126 for _, fhs := range f.File {
127 for _, fh := range fhs {
128 if fh.tmpfile != "" {
129 e := os.Remove(fh.tmpfile)
130 if e != nil && err == nil {
131 err = e
132 }
133 }
134 }
135 }
136 return err
137 }
138
139
140 type FileHeader struct {
141 Filename string
142 Header textproto.MIMEHeader
143 Size int64
144
145 content []byte
146 tmpfile string
147 }
148
149
150 func (fh *FileHeader) Open() (File, error) {
151 if b := fh.content; b != nil {
152 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
153 return sectionReadCloser{r}, nil
154 }
155 return os.Open(fh.tmpfile)
156 }
157
158
159
160
161 type File interface {
162 io.Reader
163 io.ReaderAt
164 io.Seeker
165 io.Closer
166 }
167
168
169
170 type sectionReadCloser struct {
171 *io.SectionReader
172 }
173
174 func (rc sectionReadCloser) Close() error {
175 return nil
176 }
177
View as plain text