Search code examples
gogoogle-apigo-http

Sending file to Google drive API from golang code yields error: Unsupported content with type: image/jpeg


based on the Google Drive API docs the proper way of uploading a file is:

curl -v -H 'Authorization: Bearer mytoken' -F 'metadata={"name": "test3.jpeg"};type=application/json' -F file=@jpeg_image.jpeg 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart'

Now, I need to perform the same request from golang code, but I am having a hard time translating this to golang, here is the code I am using after several attempts:

// fileBytes are of type []byte
buffer := &bytes.Buffer{}
    multipartWriter := multipart.NewWriter(buffer)
    multipartWriter.WriteField("metadata", "{\"name\": \"test3.jpef\"};type=application/json")
    partHeader := textproto.MIMEHeader{}
    partHeader.Add("Content-Type", "image/jpeg")
    filePartWriter, _ := multipartWriter.CreatePart(partHeader)
    io.Copy(filePartWriter, bytes.NewReader(fileBytes))
    multipartWriter.Close()

    googleDriveRequest, _ := http.NewRequest(http.MethodPost, "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart", buffer)
    googleDriveRequest.Header.Add("Authorization", "Bearer "+accessToken)
    googleDriveRequest.Header.Add("Content-Type", multipartWriter.FormDataContentType())

    googleDriveAPIResponse, googleDriveAPIError := lib.HttpClient.Do(googleDriveRequest)

Using curl the request succeeds :

{
 "kind": "drive#file",
 "id": "1DwsbQGP3-QtS5p0iQqRtAjcCCsAfxzGD",
 "name": "test3.jpeg",
 "mimeType": "image/jpeg"
}

With Golang, I get a 400:

{\n \"error\": {\n  \"errors\": [\n   {\n    \"domain\": \"global\",\n    \"reason\": \"badContent\",\n    \"message\": \"Unsupported content with type: image/jpeg \"\n   }\n  ],\n  \"code\": 400,\n  \"message\": \"Unsupported content with type: image/jpeg\"\n }\n}\n

I also tried with different values for partHeader.Add("Content-Type", "image/jpeg"), I tried with application/x-www-form-urlencoded as well, I also commented out this line, and let golang do the detection, but still getting the same error.

Any suggestions or ideas?

Regards,


Solution

  • The problem is in the JSON metadata that you send. Here is a sample code (use proper error checking) that will work,

    import (
        "bytes"
        "fmt"
        "io"
        "io/ioutil"
        "log"
        "mime/multipart"
        "net/http"
        "net/textproto"
    )
    
    func main() {
        accessToken := "YOUR-TOKEN"
    
        client := http.Client{}
        mediaData, _ := ioutil.ReadFile("test.png")
    
        body := &bytes.Buffer{}
        writer := multipart.NewWriter(body)
    
        // JSON Metadata (part-1)
        jsonMetadata := textproto.MIMEHeader{}
        metadata := `{"name": "test.png"}`
        jsonMetadata.Set("Content-Type", "application/json")
        part, _ := writer.CreatePart(jsonMetadata)
        part.Write([]byte(metadata))
    
        // Image bytes (part-2)
        imageData := textproto.MIMEHeader{}
        partAttach, _ := writer.CreatePart(imageData)
        io.Copy(partAttach, bytes.NewReader(mediaData))
        writer.Close()
    
        // Request Content Type with boundary
        contentType := fmt.Sprintf("multipart/related; boundary=%s", writer.Boundary())
    
        // HTTP Request with auth and content type headers
        req, _ := http.NewRequest(http.MethodPost, "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart", bytes.NewReader(body.Bytes()))
        req.Header.Add("Authorization", "Bearer "+accessToken)
        req.Header.Add("Content-Type", contentType)
    
        // Send request
        resp, err := client.Do(req)
        if err != nil {
            log.Fatalf("failed to send request: %v", err)
        }
        defer resp.Body.Close()
        content, _ := ioutil.ReadAll(resp.Body)
    
        log.Printf("http status: %d", resp.StatusCode)
        log.Printf("response: %s", string(content))
    }
    

    Reference: https://developers.google.com/drive/api/v3/manage-uploads?authuser=1#send_a_multipart_upload_request