golang formdata 代码
文件路径:/src/mime/multipart/formdata.go
/ Copyright 2011 The Go Authors. All rights reserved.
/ Use of this source code is governed by a BSD-style
/ license that can be found in the LICENSE file.
package multipart
import (
"bytes"
"errors"
"io"
"math"
"net/textproto"
"os"
)
/ ErrMessageTooLarge is returned by ReadForm if the message form
/ data is too large to be processed.
var ErrMessageTooLarge = errors.New("multipart: message too large")
/ TODO(adg,bradfitz): find a way to unify the DoS-prevention strategy here
/ with that of the http package's ParseForm.
/ ReadForm parses an entire multipart message whose parts have
/ a Content-Disposition of "form-data".
/ It stores up to maxMemory bytes + 10MB (reserved for non-file parts)
/ in memory. File parts which can't be stored in memory will be stored on
/ disk in temporary files.
/ It returns ErrMessageTooLarge if all non-file parts can't be stored in
/ memory.
func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
return r.readForm(maxMemory)
}
func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
defer func() {
if err != nil {
form.RemoveAll()
}
}()
/ Reserve an additional 10 MB for non-file parts.
maxValueBytes := maxMemory + int64(10<<20)
if maxValueBytes <= 0 {
if maxMemory < 0 {
maxValueBytes = 0
} else {
maxValueBytes = math.MaxInt64
}
}
for {
p, err := r.NextPart()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
name := p.FormName()
if name == "" {
continue
}
filename := p.FileName()
var b bytes.Buffer
if filename == "" {
/ value, store as string in memory
n, err := io.CopyN(&b, p, maxValueBytes+1)
if err != nil && err != io.EOF {
return nil, err
}
maxValueBytes -= n
if maxValueBytes < 0 {
return nil, ErrMessageTooLarge
}
form.Value[name] = append(form.Value[name], b.String())
continue
}
/ file, store in memory or on disk
fh := &FileHeader{
Filename: filename,
Header: p.Header,
}
n, err := io.CopyN(&b, p, maxMemory+1)
if err != nil && err != io.EOF {
return nil, err
}
if n > maxMemory {
/ too big, write to disk and flush buffer
file, err := os.CreateTemp("", "multipart-")
if err != nil {
return nil, err
}
size, err := io.Copy(file, io.MultiReader(&b, p))
if cerr := file.Close(); err == nil {
err = cerr
}
if err != nil {
os.Remove(file.Name())
return nil, err
}
fh.tmpfile = file.Name()
fh.Size = size
} else {
fh.content = b.Bytes()
fh.Size = int64(len(fh.content))
maxMemory -= n
maxValueBytes -= n
}
form.File[name] = append(form.File[name], fh)
}
return form, nil
}
/ Form is a parsed multipart form.
/ Its File parts are stored either in memory or on disk,
/ and are accessible via the *FileHeader's Open method.
/ Its Value parts are stored as strings.
/ Both are keyed by field name.
type Form struct {
Value map[string][]string
File map[string][]*FileHeader
}
/ RemoveAll removes any temporary files associated with a Form.
func (f *Form) RemoveAll() error {
var err error
for _, fhs := range f.File {
for _, fh := range fhs {
if fh.tmpfile != "" {
e := os.Remove(fh.tmpfile)
if e != nil && err == nil {
err = e
}
}
}
}
return err
}
/ A FileHeader describes a file part of a multipart request.
type FileHeader struct {
Filename string
Header textproto.MIMEHeader
Size int64
content []byte
tmpfile string
}
/ Open opens and returns the FileHeader's associated File.
func (fh *FileHeader) Open() (File, error) {
if b := fh.content; b != nil {
r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
return sectionReadCloser{r}, nil
}
return os.Open(fh.tmpfile)
}
/ File is an interface to access the file part of a multipart message.
/ Its contents may be either stored in memory or on disk.
/ If stored on disk, the File's underlying concrete type will be an *os.File.
type File interface {
io.Reader
io.ReaderAt
io.Seeker
io.Closer
}
/ helper types to turn a []byte into a File
type sectionReadCloser struct {
*io.SectionReader
}
func (rc sectionReadCloser) Close() error {
return nil
}