-
Notifications
You must be signed in to change notification settings - Fork 221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to upload multiple files? #85
Comments
The spec expects that there should be
Here is amended implementation of runWithPostFields func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp interface{}) error {
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)
oprField, err := operationsField(req)
if err != nil {
return errors.Wrap(err, "encode operations")
}
oprWriter, err := writer.CreateFormField("operations")
if err != nil {
return errors.Wrap(err, "encode operations")
}
_, err = oprWriter.Write(oprField)
if err != nil {
return errors.Wrap(err, "encode operations")
}
mpField, err := mapField(req, err)
if err != nil {
return errors.Wrap(err, "encode map")
}
mapWriter, err := writer.CreateFormField("map")
if err != nil {
return errors.Wrap(err, "encode map")
}
_, err = mapWriter.Write(mpField)
if err != nil {
return errors.Wrap(err, "encode map")
}
// Add files fields
for i := range req.files {
part, err := writer.CreateFormFile(req.files[i].Field, req.files[i].Name)
if err != nil {
return errors.Wrap(err, "create form file")
}
if _, err := io.Copy(part, req.files[i].R); err != nil {
return errors.Wrap(err, "preparing file")
}
}
if err := writer.Close(); err != nil {
return errors.Wrap(err, "close writer")
}
//c.logf(">> variables: %s", variablesBuf.String())
c.logf(">> files: %d", len(req.files))
c.logf(">> query: %s", req.q)
gr := &graphResponse{
Data: resp,
}
r, err := http.NewRequest(http.MethodPost, c.endpoint, &requestBody)
if err != nil {
return err
}
r.Close = c.closeReq
r.Header.Set("Content-Type", writer.FormDataContentType())
r.Header.Set("Accept", "application/json; charset=utf-8")
for key, values := range req.Header {
for _, value := range values {
r.Header.Add(key, value)
}
}
c.logf(">> headers: %v", r.Header)
r = r.WithContext(ctx)
res, err := c.httpClient.Do(r)
if err != nil {
return err
}
defer res.Body.Close()
var buf bytes.Buffer
if _, err := io.Copy(&buf, res.Body); err != nil {
return errors.Wrap(err, "reading body")
}
c.logf("<< %s", buf.String())
if err := json.NewDecoder(&buf).Decode(&gr); err != nil {
if res.StatusCode != http.StatusOK {
return fmt.Errorf("graphql: server returned a non-200 status code: %v", res.StatusCode)
}
return errors.Wrap(err, "decoding response")
}
if len(gr.Errors) > 0 {
// return first error
return gr.Errors[0]
}
return nil
}
// mapField build map field of graphQL multipart query - returns:
//
// { "0": ["variables.files.0"], "1": ["variables.files.1"] }
func mapField(req *Request, err error) ([]byte, error) {
mapField := map[int][]string{}
for i := 0; i < len(req.files); i++ {
mapField[i] = []string{fmt.Sprintf("variables.files.%d", i)}
}
bt, err := json.Marshal(mapField)
if err != nil {
return nil, err
}
return bt, err
}
// operationsField build operations field of graphQL multipart query returns:
//
// { "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }",
//
// "variables": { "file": null } }'
func operationsField(req *Request) ([]byte, error) {
opm := map[string]interface{}{}
opm["query"] = req.q
flsVars := make([]string, len(req.files))
for i, _ := range flsVars {
flsVars[i] = "null"
}
opm["variables"] = map[string][]string{"files": flsVars}
bt, err := json.Marshal(opm)
if err != nil {
return nil, err
}
return bt, err
}
/ File sets a file to upload.
// Files are only supported with a Client that was created with
// the UseMultipartForm option.
//
// In accordance with https://github.com/jaydenseric/graphql-multipart-request-spec
// files should be added in the following form:
//
// "0", "fileName0.txt, fileReader0"
//
// "1", "fileName1.txt, fileReader1
//
// "N", "fileNameN.txt, fileReaderN
func (req *Request) File(fieldname, filename string, r io.Reader) {
req.files = append(req.files, File{
Field: fieldname, Name: filename, R: r,
})
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I try to do it in following way:
... and get error -
graphql: first part must be operations
.PS: without
.UseMultipartForm()
usual read queries go wellThe text was updated successfully, but these errors were encountered: