commands: Added File interface and implementations
Matt Bell committed
Nov 16, 2014 at 00:04 UTC
02c7c329aa17f8cf7c1e5b79762554010a9cad07
1 file changed
+156
commands/file.go
new
+156
@@ -0,0 +1,156 @@
1
+package commands
2
+
3
+import (
4
+ "errors"
5
+ "io"
6
+ "mime"
7
+ "mime/multipart"
8
+ "net/http"
9
+)
10
+
11
+const (
12
+ multipartFormdataType = "multipart/form-data"
13
+ multipartMixedType = "multipart/mixed"
14
+
15
+ contentTypeHeader = "Content-Type"
16
+)
17
+
18
+var (
19
+ ErrNotDirectory = errors.New("Couln't call NextFile(), this isn't a directory")
20
+ ErrNotReader = errors.New("This file is a directory, can't use Reader functions")
21
+)
22
+
23
+type File interface {
24
+ io.ReadCloser
25
+ FileName() string
26
+ IsDirectory() bool
27
+ NextFile() (File, error)
28
+}
29
+
30
+type MultipartFile struct {
31
+ File
32
+
33
+ Part *multipart.Part
34
+ Reader *multipart.Reader
35
+ Mediatype string
36
+}
37
+
38
+func NewFileFromPart(part *multipart.Part) (File, error) {
39
+ f := &MultipartFile{
40
+ Part: part,
41
+ }
42
+
43
+ contentType := part.Header.Get(contentTypeHeader)
44
+
45
+ var params map[string]string
46
+ var err error
47
+ f.Mediatype, params, err = mime.ParseMediaType(contentType)
48
+ if err != nil {
49
+ return nil, err
50
+ }
51
+
52
+ if f.IsDirectory() {
53
+ boundary, found := params["boundary"]
54
+ if !found {
55
+ return nil, http.ErrMissingBoundary
56
+ }
57
+
58
+ f.Reader = multipart.NewReader(part, boundary)
59
+ }
60
+
61
+ return f, nil
62
+}
63
+
64
+func (f *MultipartFile) IsDirectory() bool {
65
+ return f.Mediatype == multipartFormdataType || f.Mediatype == multipartMixedType
66
+}
67
+
68
+func (f *MultipartFile) NextFile() (File, error) {
69
+ if !f.IsDirectory() {
70
+ return nil, ErrNotDirectory
71
+ }
72
+
73
+ part, err := f.Reader.NextPart()
74
+ if err != nil {
75
+ return nil, err
76
+ }
77
+
78
+ return NewFileFromPart(part)
79
+}
80
+
81
+func (f *MultipartFile) FileName() string {
82
+ return f.Part.FileName()
83
+}
84
+
85
+func (f *MultipartFile) Read(p []byte) (int, error) {
86
+ if f.IsDirectory() {
87
+ return 0, ErrNotReader
88
+ }
89
+ return f.Part.Read(p)
90
+}
91
+
92
+func (f *MultipartFile) Close() error {
93
+ if f.IsDirectory() {
94
+ return ErrNotReader
95
+ }
96
+ return f.Part.Close()
97
+}
98
+
99
+type SliceFile struct {
100
+ Filename string
101
+ Files []File
102
+}
103
+
104
+func (f *SliceFile) IsDirectory() bool {
105
+ return true
106
+}
107
+
108
+func (f *SliceFile) NextFile() (File, error) {
109
+ if len(f.Files) == 0 {
110
+ return nil, io.EOF
111
+ }
112
+ file := f.Files[0]
113
+ f.Files = f.Files[1:]
114
+ return file, nil
115
+}
116
+
117
+func (f *SliceFile) FileName() string {
118
+ return f.Filename
119
+}
120
+
121
+func (f *SliceFile) Read(p []byte) (int, error) {
122
+ return 0, ErrNotReader
123
+}
124
+
125
+func (f *SliceFile) Close() error {
126
+ return ErrNotReader
127
+}
128
+
129
+type ReaderFile struct {
130
+ Filename string
131
+ Reader io.Reader
132
+}
133
+
134
+func (f *ReaderFile) IsDirectory() bool {
135
+ return false
136
+}
137
+
138
+func (f *ReaderFile) NextFile() (File, error) {
139
+ return nil, ErrNotDirectory
140
+}
141
+
142
+func (f *ReaderFile) FileName() string {
143
+ return f.Filename
144
+}
145
+
146
+func (f *ReaderFile) Read(p []byte) (int, error) {
147
+ return f.Reader.Read(p)
148
+}
149
+
150
+func (f *ReaderFile) Close() error {
151
+ closer, ok := f.Reader.(io.Closer)
152
+ if !ok {
153
+ return nil
154
+ }
155
+ return closer.Close()
156
+}