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

View as plain text