...
Run Format

Source file 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"
  	"io/ioutil"
  	"net/textproto"
  	"os"
  )
  
  // 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 of the file parts in memory
  // and the remainder on disk in temporary files.
  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()
  		}
  	}()
  
  	maxValueBytes := int64(10 << 20) // 10 MB is a lot of text.
  	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)
  			if err != nil && err != io.EOF {
  				return nil, err
  			}
  			maxValueBytes -= n
  			if maxValueBytes == 0 {
  				return nil, errors.New("multipart: message too large")
  			}
  			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 := ioutil.TempFile("", "multipart-")
  			if err != nil {
  				return nil, err
  			}
  			_, 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()
  		} else {
  			fh.content = b.Bytes()
  			maxMemory -= 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
  
  	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
  }
  

View as plain text