rest: implement multipart uploads

This commit is contained in:
Nick Craig-Wood 2017-07-05 17:48:43 +01:00
parent edfa1b3a69
commit 45ba4ed594

View file

@ -8,6 +8,7 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil" "io/ioutil"
"mime/multipart"
"net/http" "net/http"
"sync" "sync"
@ -90,6 +91,15 @@ type Opts struct {
Password string // password for Basic Auth Password string // password for Basic Auth
Options []fs.OpenOption Options []fs.OpenOption
IgnoreStatus bool // if set then we don't check error status or parse error body IgnoreStatus bool // if set then we don't check error status or parse error body
MultipartMetadataName string // set the following 3 vars
MultipartContentName string // and Body and pass in request
MultipartFileName string // for multipart upload
}
// Copy creates a copy of the options
func (o *Opts) Copy() *Opts {
newOpts := *o
return &newOpts
} }
// DecodeJSON decodes resp.Body into result // DecodeJSON decodes resp.Body into result
@ -202,16 +212,53 @@ func (api *Client) Call(opts *Opts) (resp *http.Response, err error) {
// //
// It will return resp if at all possible, even if err is set // It will return resp if at all possible, even if err is set
func (api *Client) CallJSON(opts *Opts, request interface{}, response interface{}) (resp *http.Response, err error) { func (api *Client) CallJSON(opts *Opts, request interface{}, response interface{}) (resp *http.Response, err error) {
// Set the body up as a JSON object if required var requestBody []byte
if opts.Body == nil && request != nil { // Marshal the request if given
body, err := json.Marshal(request) if request != nil {
opts = opts.Copy()
requestBody, err = json.Marshal(request)
opts.ContentType = "application/json"
if err != nil { if err != nil {
return nil, err return nil, err
} }
var newOpts = *opts // Set the body up as a JSON object if required
newOpts.Body = bytes.NewBuffer(body) if opts.Body == nil {
newOpts.ContentType = "application/json" opts.Body = bytes.NewBuffer(requestBody)
opts = &newOpts }
}
errChan := make(chan error, 1)
isMultipart := opts.MultipartMetadataName != "" && opts.Body != nil && request != nil
if isMultipart {
bodyReader, bodyWriter := io.Pipe()
writer := multipart.NewWriter(bodyWriter)
opts.ContentType = writer.FormDataContentType()
in := opts.Body
opts.Body = bodyReader
go func() {
defer func() { _ = bodyWriter.Close() }()
var err error
// Create the first part
err = writer.WriteField(opts.MultipartMetadataName, string(requestBody))
if err != nil {
errChan <- err
return
}
// Add the file part
part, err := writer.CreateFormFile(opts.MultipartContentName, opts.MultipartFileName)
if err != nil {
errChan <- err
return
}
// Copy it in
if _, err := io.Copy(part, in); err != nil {
errChan <- err
return
}
errChan <- writer.Close()
}()
} }
resp, err = api.Call(opts) resp, err = api.Call(opts)
if err != nil { if err != nil {
@ -220,6 +267,12 @@ func (api *Client) CallJSON(opts *Opts, request interface{}, response interface{
if response == nil || opts.NoResponse { if response == nil || opts.NoResponse {
return resp, nil return resp, nil
} }
if isMultipart {
err = <-errChan
if err != nil {
return resp, err
}
}
err = DecodeJSON(resp, response) err = DecodeJSON(resp, response)
return resp, err return resp, err
} }