form.go raw
1 package dara
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "math/rand"
8 "net/url"
9 "strings"
10 )
11
12 type FileField struct {
13 Filename *string `json:"filename" xml:"filename" require:"true"`
14 ContentType *string `json:"content-type" xml:"content-type" require:"true"`
15 Content io.Reader `json:"content" xml:"content" require:"true"`
16 }
17
18 func (s *FileField) SetFilename(v string) *FileField {
19 s.Filename = &v
20 return s
21 }
22
23 func (s *FileField) SetContentType(v string) *FileField {
24 s.ContentType = &v
25 return s
26 }
27
28 func (s *FileField) SetContent(v io.Reader) *FileField {
29 s.Content = v
30 return s
31 }
32
33 type FileFormReader struct {
34 formFiles []*formFile
35 formField io.Reader
36 index int
37 streaming bool
38 ifField bool
39 }
40
41 type formFile struct {
42 StartField io.Reader
43 EndField io.Reader
44 File io.Reader
45 start bool
46 end bool
47 }
48
49 const numBytes = "1234567890"
50
51 func GetBoundary() string {
52 b := make([]byte, 14)
53 for i := range b {
54 b[i] = numBytes[rand.Intn(len(numBytes))]
55 }
56 return string(b)
57 }
58
59 func ToFileForm(body map[string]interface{}, boundary string) io.Reader {
60 out := bytes.NewBuffer(nil)
61 line := "--" + boundary + "\r\n"
62 forms := make(map[string]string)
63 files := make(map[string]map[string]interface{})
64 for key, value := range body {
65 switch value.(type) {
66 case *FileField:
67 if val, ok := value.(*FileField); ok {
68 out := make(map[string]interface{})
69 out["filename"] = StringValue(val.Filename)
70 out["content-type"] = StringValue(val.ContentType)
71 out["content"] = val.Content
72 files[key] = out
73 }
74 case map[string]interface{}:
75 if val, ok := value.(map[string]interface{}); ok {
76 files[key] = val
77 }
78 default:
79 forms[key] = fmt.Sprintf("%v", value)
80 }
81 }
82 for key, value := range forms {
83 if value != "" {
84 out.Write([]byte(line))
85 out.Write([]byte("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n\r\n"))
86 out.Write([]byte(value + "\r\n"))
87 }
88 }
89 formFiles := make([]*formFile, 0)
90 for key, value := range files {
91 var file io.Reader
92 start := line
93 start += "Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + value["filename"].(string) + "\"\r\n"
94 start += "Content-Type: " + value["content-type"].(string) + "\r\n\r\n"
95 if content, ok := value["content"].(io.Reader); ok {
96 file = content
97 } else {
98 file = strings.NewReader("")
99 }
100 formFile := &formFile{
101 File: file,
102 start: true,
103 StartField: strings.NewReader(start),
104 }
105 if len(files) == len(formFiles)+1 {
106 end := "\r\n\r\n--" + boundary + "--\r\n"
107 formFile.EndField = strings.NewReader(end)
108 } else {
109 formFile.EndField = strings.NewReader("\r\n\r\n")
110 }
111 formFiles = append(formFiles, formFile)
112 }
113 return &FileFormReader{
114 formFiles: formFiles,
115 formField: out,
116 ifField: true,
117 }
118 }
119
120 func (f *FileFormReader) Read(p []byte) (n int, err error) {
121 if f.ifField {
122 n, err = f.formField.Read(p)
123 if err != nil && err != io.EOF {
124 return n, err
125 } else if err == io.EOF {
126 err = nil
127 f.ifField = false
128 f.streaming = true
129 }
130 } else if f.streaming {
131 form := f.formFiles[f.index]
132 if form.start {
133 n, err = form.StartField.Read(p)
134 if err != nil && err != io.EOF {
135 return n, err
136 } else if err == io.EOF {
137 err = nil
138 form.start = false
139 }
140 } else if form.end {
141 n, err = form.EndField.Read(p)
142 if err != nil && err != io.EOF {
143 return n, err
144 } else if err == io.EOF {
145 f.index++
146 form.end = false
147 if f.index < len(f.formFiles) {
148 err = nil
149 }
150 }
151 } else {
152 n, err = form.File.Read(p)
153 if err != nil && err != io.EOF {
154 return n, err
155 } else if err == io.EOF {
156 err = nil
157 form.end = true
158 }
159 }
160 }
161
162 return n, err
163 }
164
165 func ToFormString(a map[string]interface{}) string {
166 if a == nil {
167 return ""
168 }
169 res := ""
170 urlEncoder := url.Values{}
171 for key, value := range a {
172 v := fmt.Sprintf("%v", value)
173 urlEncoder.Add(key, v)
174 }
175 res = urlEncoder.Encode()
176 return res
177 }
178