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