forked from masci/flickr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
upload.go
175 lines (148 loc) · 4.35 KB
/
upload.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package flickr
import (
"crypto/rand"
"fmt"
"io"
"log"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
)
// generate a random multipart boundary string,
// shamelessly copypasted from the std library
func randomBoundary() string {
var buf [30]byte
_, err := io.ReadFull(rand.Reader, buf[:])
if err != nil {
panic(err)
}
return fmt.Sprintf("%x", buf[:])
}
// Encode the file and request parameters in a multipart body.
// File contents are streamed into the request using an io.Pipe in a separated goroutine
func streamUploadBody(client *FlickrClient, photo io.Reader, body *io.PipeWriter, fileName string, boundary string) {
// multipart writer to fill the body
defer body.Close()
writer := multipart.NewWriter(body)
writer.SetBoundary(boundary)
// create the "photo" field
part, err := writer.CreateFormFile("photo", filepath.Base(fileName))
if err != nil {
log.Fatal(err)
return
}
// fill the photo field
_, err = io.Copy(part, photo)
if err != nil {
log.Fatal(err)
return
}
// dump other params
for key, val := range client.Args {
_ = writer.WriteField(key, val[0])
}
// close the form writer
err = writer.Close()
if err != nil {
log.Fatal(err)
return
}
}
// A convenience struct wrapping all optional upload parameters
type UploadParams struct {
Title, Description string
Tags []string
IsPublic, IsFamily, IsFriend bool
ContentType int
Hidden int
SafetyLevel int
}
// Provide meaningful default values
func NewUploadParams() *UploadParams {
ret := &UploadParams{}
ret.ContentType = 1 // photo
ret.Hidden = 2 // hidden from public searchesi
ret.SafetyLevel = 1 // safe
return ret
}
// Type representing a successful upload response from the api
type UploadResponse struct {
BasicResponse
Id string `xml:"photoid"`
}
// Set client query arguments based on the contents of the UploadParams struct
func fillArgsWithParams(client *FlickrClient, params *UploadParams) {
if params.Title != "" {
client.Args.Set("title", params.Title)
}
if params.Description != "" {
client.Args.Set("description", params.Description)
}
if len(params.Tags) > 0 {
client.Args.Set("tags", strings.Join(params.Tags, " "))
}
var boolString = func(b bool) string {
if b {
return "1"
}
return "0"
}
client.Args.Set("is_public", boolString(params.IsPublic))
client.Args.Set("is_friend", boolString(params.IsFriend))
client.Args.Set("is_family", boolString(params.IsFamily))
if params.ContentType >= 1 && params.ContentType <= 3 {
client.Args.Set("content_type", strconv.Itoa(params.ContentType))
}
if params.Hidden >= 1 && params.Hidden <= 2 {
client.Args.Set("hidden", strconv.Itoa(params.Hidden))
}
if params.SafetyLevel >= 1 && params.SafetyLevel <= 3 {
client.Args.Set("safety_level", strconv.Itoa(params.SafetyLevel))
}
}
// Perform a file upload using the Flickr API. If optionalParams is nil,
// no parameters will be added to the request and Flickr will set User's
// default preferences.
// This call must be signed with write permissions
func UploadFile(client *FlickrClient, path string, optionalParams *UploadParams) (*UploadResponse, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return UploadReader(client, file, file.Name(), optionalParams)
}
// Same as UploadFile but the photo file is passed as an io.Reader instead of a file path
func UploadReader(client *FlickrClient, photoReader io.Reader, name string, optionalParams *UploadParams) (*UploadResponse, error) {
client.Init()
client.EndpointUrl = UPLOAD_ENDPOINT
client.HTTPVerb = "POST"
if optionalParams != nil {
fillArgsWithParams(client, optionalParams)
}
client.OAuthSign()
// write request body in a Pipe
boundary := randomBoundary()
r, w := io.Pipe()
go streamUploadBody(client, photoReader, w, name, boundary)
// create an HTTP Request
req, err := http.NewRequest("POST", client.EndpointUrl, r)
if err != nil {
return nil, err
}
// set content-type
req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundary)
// instance an HTTP client
http_client := &http.Client{}
// perform upload request streaming the file
resp, err := http_client.Do(req)
if err != nil {
return nil, err
}
api_resp := &UploadResponse{}
err = parseApiResponse(resp, api_resp)
return api_resp, err
}