github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/commands/files/multipartfile.go (about)

     1  package files
     2  
     3  import (
     4  	"io/ioutil"
     5  	"mime"
     6  	"mime/multipart"
     7  	"net/http"
     8  	"net/url"
     9  )
    10  
    11  const (
    12  	multipartFormdataType = "multipart/form-data"
    13  	multipartMixedType    = "multipart/mixed"
    14  
    15  	applicationSymlink = "application/symlink"
    16  
    17  	contentTypeHeader = "Content-Type"
    18  )
    19  
    20  // MultipartFile implements File, and is created from a `multipart.Part`.
    21  // It can be either a directory or file (checked by calling `IsDirectory()`).
    22  type MultipartFile struct {
    23  	File
    24  
    25  	Part      *multipart.Part
    26  	Reader    *multipart.Reader
    27  	Mediatype string
    28  }
    29  
    30  func NewFileFromPart(part *multipart.Part) (File, error) {
    31  	f := &MultipartFile{
    32  		Part: part,
    33  	}
    34  
    35  	contentType := part.Header.Get(contentTypeHeader)
    36  	if contentType == applicationSymlink {
    37  		out, err := ioutil.ReadAll(part)
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  
    42  		return &Symlink{
    43  			Target: string(out),
    44  			name:   f.FileName(),
    45  		}, nil
    46  	}
    47  
    48  	var params map[string]string
    49  	var err error
    50  	f.Mediatype, params, err = mime.ParseMediaType(contentType)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	if f.IsDirectory() {
    56  		boundary, found := params["boundary"]
    57  		if !found {
    58  			return nil, http.ErrMissingBoundary
    59  		}
    60  
    61  		f.Reader = multipart.NewReader(part, boundary)
    62  	}
    63  
    64  	return f, nil
    65  }
    66  
    67  func (f *MultipartFile) IsDirectory() bool {
    68  	return f.Mediatype == multipartFormdataType || f.Mediatype == multipartMixedType
    69  }
    70  
    71  func (f *MultipartFile) NextFile() (File, error) {
    72  	if !f.IsDirectory() {
    73  		return nil, ErrNotDirectory
    74  	}
    75  
    76  	part, err := f.Reader.NextPart()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	return NewFileFromPart(part)
    82  }
    83  
    84  func (f *MultipartFile) FileName() string {
    85  	if f == nil || f.Part == nil {
    86  		return ""
    87  	}
    88  
    89  	filename, err := url.QueryUnescape(f.Part.FileName())
    90  	if err != nil {
    91  		// if there is a unescape error, just treat the name as unescaped
    92  		return f.Part.FileName()
    93  	}
    94  	return filename
    95  }
    96  
    97  func (f *MultipartFile) FullPath() string {
    98  	return f.FileName()
    99  }
   100  
   101  func (f *MultipartFile) Read(p []byte) (int, error) {
   102  	if f.IsDirectory() {
   103  		return 0, ErrNotReader
   104  	}
   105  	return f.Part.Read(p)
   106  }
   107  
   108  func (f *MultipartFile) Close() error {
   109  	if f.IsDirectory() {
   110  		return ErrNotReader
   111  	}
   112  	return f.Part.Close()
   113  }