...
Run Format

Source file src/mime/multipart/formdata.go

     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				_, 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			} else {
    92				fh.content = b.Bytes()
    93				maxMemory -= n
    94			}
    95			form.File[name] = append(form.File[name], fh)
    96		}
    97	
    98		return form, nil
    99	}
   100	
   101	// Form is a parsed multipart form.
   102	// Its File parts are stored either in memory or on disk,
   103	// and are accessible via the *FileHeader's Open method.
   104	// Its Value parts are stored as strings.
   105	// Both are keyed by field name.
   106	type Form struct {
   107		Value map[string][]string
   108		File  map[string][]*FileHeader
   109	}
   110	
   111	// RemoveAll removes any temporary files associated with a Form.
   112	func (f *Form) RemoveAll() error {
   113		var err error
   114		for _, fhs := range f.File {
   115			for _, fh := range fhs {
   116				if fh.tmpfile != "" {
   117					e := os.Remove(fh.tmpfile)
   118					if e != nil && err == nil {
   119						err = e
   120					}
   121				}
   122			}
   123		}
   124		return err
   125	}
   126	
   127	// A FileHeader describes a file part of a multipart request.
   128	type FileHeader struct {
   129		Filename string
   130		Header   textproto.MIMEHeader
   131	
   132		content []byte
   133		tmpfile string
   134	}
   135	
   136	// Open opens and returns the FileHeader's associated File.
   137	func (fh *FileHeader) Open() (File, error) {
   138		if b := fh.content; b != nil {
   139			r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
   140			return sectionReadCloser{r}, nil
   141		}
   142		return os.Open(fh.tmpfile)
   143	}
   144	
   145	// File is an interface to access the file part of a multipart message.
   146	// Its contents may be either stored in memory or on disk.
   147	// If stored on disk, the File's underlying concrete type will be an *os.File.
   148	type File interface {
   149		io.Reader
   150		io.ReaderAt
   151		io.Seeker
   152		io.Closer
   153	}
   154	
   155	// helper types to turn a []byte into a File
   156	
   157	type sectionReadCloser struct {
   158		*io.SectionReader
   159	}
   160	
   161	func (rc sectionReadCloser) Close() error {
   162		return nil
   163	}
   164	

View as plain text