src/pkg/mime/multipart/formdata.go - The Go Programming Language

Golang

Source file src/pkg/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) (f *Form, err error) {
    24		form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
    25		defer func() {
    26			if err != nil {
    27				form.RemoveAll()
    28			}
    29		}()
    30	
    31		maxValueBytes := int64(10 << 20) // 10 MB is a lot of text.
    32		for {
    33			p, err := r.NextPart()
    34			if err == io.EOF {
    35				break
    36			}
    37			if err != nil {
    38				return nil, err
    39			}
    40	
    41			name := p.FormName()
    42			if name == "" {
    43				continue
    44			}
    45			filename := p.FileName()
    46	
    47			var b bytes.Buffer
    48	
    49			if filename == "" {
    50				// value, store as string in memory
    51				n, err := io.CopyN(&b, p, maxValueBytes)
    52				if err != nil && err != io.EOF {
    53					return nil, err
    54				}
    55				maxValueBytes -= n
    56				if maxValueBytes == 0 {
    57					return nil, errors.New("multipart: message too large")
    58				}
    59				form.Value[name] = append(form.Value[name], b.String())
    60				continue
    61			}
    62	
    63			// file, store in memory or on disk
    64			fh := &FileHeader{
    65				Filename: filename,
    66				Header:   p.Header,
    67			}
    68			n, err := io.CopyN(&b, p, maxMemory+1)
    69			if err != nil && err != io.EOF {
    70				return nil, err
    71			}
    72			if n > maxMemory {
    73				// too big, write to disk and flush buffer
    74				file, err := ioutil.TempFile("", "multipart-")
    75				if err != nil {
    76					return nil, err
    77				}
    78				defer file.Close()
    79				_, err = io.Copy(file, io.MultiReader(&b, p))
    80				if err != nil {
    81					os.Remove(file.Name())
    82					return nil, err
    83				}
    84				fh.tmpfile = file.Name()
    85			} else {
    86				fh.content = b.Bytes()
    87				maxMemory -= n
    88			}
    89			form.File[name] = append(form.File[name], fh)
    90		}
    91	
    92		return form, nil
    93	}
    94	
    95	// Form is a parsed multipart form.
    96	// Its File parts are stored either in memory or on disk,
    97	// and are accessible via the *FileHeader's Open method.
    98	// Its Value parts are stored as strings.
    99	// Both are keyed by field name.
   100	type Form struct {
   101		Value map[string][]string
   102		File  map[string][]*FileHeader
   103	}
   104	
   105	// RemoveAll removes any temporary files associated with a Form.
   106	func (f *Form) RemoveAll() error {
   107		var err error
   108		for _, fhs := range f.File {
   109			for _, fh := range fhs {
   110				if fh.tmpfile != "" {
   111					e := os.Remove(fh.tmpfile)
   112					if e != nil && err == nil {
   113						err = e
   114					}
   115				}
   116			}
   117		}
   118		return err
   119	}
   120	
   121	// A FileHeader describes a file part of a multipart request.
   122	type FileHeader struct {
   123		Filename string
   124		Header   textproto.MIMEHeader
   125	
   126		content []byte
   127		tmpfile string
   128	}
   129	
   130	// Open opens and returns the FileHeader's associated File.
   131	func (fh *FileHeader) Open() (File, error) {
   132		if b := fh.content; b != nil {
   133			r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
   134			return sectionReadCloser{r}, nil
   135		}
   136		return os.Open(fh.tmpfile)
   137	}
   138	
   139	// File is an interface to access the file part of a multipart message.
   140	// Its contents may be either stored in memory or on disk.
   141	// If stored on disk, the File's underlying concrete type will be an *os.File.
   142	type File interface {
   143		io.Reader
   144		io.ReaderAt
   145		io.Seeker
   146		io.Closer
   147	}
   148	
   149	// helper types to turn a []byte into a File
   150	
   151	type sectionReadCloser struct {
   152		*io.SectionReader
   153	}
   154	
   155	func (rc sectionReadCloser) Close() error {
   156		return nil
   157	}