1 package client
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "io"
8 "mime/multipart"
9 "net/http"
10 "net/textproto"
11 "os"
12 "strings"
13 )
14
15 type fileFormDataMap struct {
16 mapKey string
17 file *os.File
18 }
19
20 func findFiles(parentMapKey string, variables map[string]interface{}) []*fileFormDataMap {
21 files := []*fileFormDataMap{}
22 for key, value := range variables {
23 if v, ok := value.(map[string]interface{}); ok {
24 files = append(files, findFiles(parentMapKey+"."+key, v)...)
25 } else if v, ok := value.([]map[string]interface{}); ok {
26 for i, arr := range v {
27 files = append(files, findFiles(fmt.Sprintf(`%s.%s.%d`, parentMapKey, key, i), arr)...)
28 }
29 } else if v, ok := value.([]*os.File); ok {
30 for i, file := range v {
31 files = append(files, &fileFormDataMap{
32 mapKey: fmt.Sprintf(`%s.%s.%d`, parentMapKey, key, i),
33 file: file,
34 })
35 }
36 } else if v, ok := value.(*os.File); ok {
37 files = append(files, &fileFormDataMap{
38 mapKey: parentMapKey + "." + key,
39 file: v,
40 })
41 }
42 }
43
44 return files
45 }
46
47
48 func WithFiles() Option {
49 return func(bd *Request) {
50 bodyBuf := &bytes.Buffer{}
51 bodyWriter := multipart.NewWriter(bodyBuf)
52
53
54
55
56
57 requestBody, _ := json.Marshal(bd)
58 bodyWriter.WriteField("operations", string(requestBody))
59
60
61
62
63
64
65
66
67
68
69
70 mapData := ""
71 filesData := findFiles("variables", bd.Variables)
72 filesGroup := [][]*fileFormDataMap{}
73 for _, fd := range filesData {
74 foundDuplicate := false
75 for j, fg := range filesGroup {
76 f1, _ := fd.file.Stat()
77 f2, _ := fg[0].file.Stat()
78 if os.SameFile(f1, f2) {
79 foundDuplicate = true
80 filesGroup[j] = append(filesGroup[j], fd)
81 }
82 }
83
84 if !foundDuplicate {
85 filesGroup = append(filesGroup, []*fileFormDataMap{fd})
86 }
87 }
88 if len(filesGroup) > 0 {
89 mapDataFiles := []string{}
90
91 for i, fileData := range filesGroup {
92 mapDataFiles = append(
93 mapDataFiles,
94 fmt.Sprintf(`"%d":[%s]`, i, strings.Join(collect(fileData, wrapMapKeyInQuotes), ",")),
95 )
96 }
97
98 mapData = `{` + strings.Join(mapDataFiles, ",") + `}`
99 }
100 bodyWriter.WriteField("map", mapData)
101
102
103
104
105
106
107
108 for i, fileData := range filesGroup {
109 h := make(textproto.MIMEHeader)
110 h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%d"; filename="%s"`, i, fileData[0].file.Name()))
111 b, _ := os.ReadFile(fileData[0].file.Name())
112 h.Set("Content-Type", http.DetectContentType(b))
113 ff, _ := bodyWriter.CreatePart(h)
114 ff.Write(b)
115 }
116 bodyWriter.Close()
117
118 bd.HTTP.Body = io.NopCloser(bodyBuf)
119 bd.HTTP.Header.Set("Content-Type", bodyWriter.FormDataContentType())
120 }
121 }
122
123 func collect(strArr []*fileFormDataMap, f func(s *fileFormDataMap) string) []string {
124 result := make([]string, len(strArr))
125 for i, str := range strArr {
126 result[i] = f(str)
127 }
128 return result
129 }
130
131 func wrapMapKeyInQuotes(s *fileFormDataMap) string {
132 return fmt.Sprintf("\"%s\"", s.mapKey)
133 }
134
View as plain text