Search code examples
gogoogle-drive-apimime-types

Uploading a file to google drive via API fails


I am trying to upload a file to my Google Drive, however it fails. I think I've specified the MIME type correctly, as I've seen that this is a common problem, but it still doesn't work for me.

About the conversion systems: I have an API with Gin-Gonic (v1.9.1) where I upload a file. The file is successfully passed from the frontend/postman to the API, since I can successfully save the file I get from the API.

The error I get is:

Post "https://www.googleapis.com/upload/drive/v3/files?alt=json&prettyPrint=false&uploadType=multipart": Post "": unsupported protocol scheme ""

I have the following function:

func (c *Client) UploadFile(oauthTokenConfig GoogleOauthTokenConfig, parentFolderId string, fileHeader *multipart.FileHeader) (*string, error) {
    svc, err := drive.NewService(context.Background(), option.WithHTTPClient(
        oauthTokenConfig.Config.Client(
            context.Background(),
            &oauth2.Token{
                AccessToken:  oauthTokenConfig.AccessToken,
                TokenType:    oauthTokenConfig.TokenType,
                RefreshToken: oauthTokenConfig.RefreshToken,
                Expiry:       oauthTokenConfig.ExpiresIn,
            },
        )),
    )
    if err != nil {
        return nil, err
    }

    fileExtension := filepath.Ext(fileHeader.Filename)
    fileName := strings.TrimSuffix(fileHeader.Filename, fileExtension)
    fileMimeType := fileHeader.Header.Get("Content-Type")
    uploadFileMetaData := drive.File{
        Name:     fmt.Sprintf("%s%s", fileName, fileExtension), 
                // fmt.Sprintf("%s_%s%s", fileName, uuid.New().String(), fileExtension),
        Parents:  []string{parentFolderId},
        MimeType: fileMimeType,
    }

    file, err := fileHeader.Open()
    if err != nil {
        return nil, err
    }
    defer file.Close()

    fileResult, err := svc.Files.
        Create(&uploadFileMetaData).
        Media(file, googleapi.ContentType("text/plain")).
        Do()
    if err != nil {
        return nil, err // here I get the error
    }

        // ...

}

I have added here the MIME type hardcoded however the variable fileMimeType is actually correct. I upload a Test.txt file with the content Test1 (the file is also created successfully when I send it via Frontend/Postman). I have also tried to dynamically specify the file MIME type or no MIME-type at all, but always with the same result.


I use the following packages for this:

  • go version: go1.21.1 darwin/arm64
  • go list -m golang.org/x/oauth2: v0.13.0
  • go list -m google.golang.org/api: v0.147.0
    • google.golang.org/api/drive/v3
    • google.golang.org/api/googleapi
    • google.golang.org/api/option

EDIT:

I have also replicated the official example from Google and it still doesn't work.


Solution

  • It looked like the error was with the authentication. It's a bit difficult to deduce the invalid authentication from this error, but I had to change the refresh algorithm for the refresh token a bit to make it work.


    Here is my working code. Note that before I call the UploadFile() function, I first check the oauthTokenConfig.ExpiresIn to see if the token is still valid, if so I upload the file, otherwise I first refresh the token.

    File-upload

    func (c *Client) UploadFile(oauthTokenConfig GoogleOauthTokenConfig, parentFolderId string, file *multipart.FileHeader) (*string, error) {
        svc, err := drive.NewService(context.Background(), option.WithHTTPClient(
            oauthTokenConfig.Config.Client(
                context.Background(),
                &oauth2.Token{
                    AccessToken:  oauthTokenConfig.AccessToken,
                    TokenType:    oauthTokenConfig.TokenType,
                    RefreshToken: oauthTokenConfig.RefreshToken,
                    Expiry:       oauthTokenConfig.ExpiresIn,
                },
            )),
        )
        if err != nil {
            return nil, fmt.Errorf("failed to create drive-service: %s", err.Error())
        }
    
        fileExtension := filepath.Ext(file.Filename)
        fileName := strings.TrimSuffix(file.Filename, fileExtension)
        uploadFile := drive.File{
            Name:    fmt.Sprintf("%s_%s%s", fileName, uuid.New().String(), fileExtension),
            Parents: []string{parentFolderId},
        }
        fileContent, err := file.Open()
        if err != nil {
            return nil, fmt.Errorf("failed to open file: %s", err.Error())
        }
    
        fileResult, err := svc.Files.Create(&uploadFile).Media(fileContent).Do()
        if err != nil {
            return nil, fmt.Errorf("failed to create file: %s", err.Error())
        }
    
        uploadedFile, err := svc.Files.Get(fileResult.Id).Fields("webViewLink").Do()
        if err != nil {
            return nil, fmt.Errorf("failed to get file: %s", err.Error())
        }
        return &uploadedFile.WebViewLink, nil
    }
    

    Refresh-token

    func (c *Client) RefreshToken(oauthTokenConfig GoogleOauthTokenConfig) (*GoogleOauthTokenConfig, error) {
        ctx := context.Background()
        config := oauth2.Config{
            ClientID:     c.ClientId,
            ClientSecret: c.ClientSecret,
            RedirectURL:  oauthTokenConfig.Config.RedirectURL,
            Scopes:       []string{"https://www.googleapis.com/auth/drive"},
            Endpoint:     google.Endpoint,
        }
    
        token := &oauth2.Token{
            AccessToken:  oauthTokenConfig.AccessToken,
            TokenType:    oauthTokenConfig.TokenType,
            RefreshToken: oauthTokenConfig.RefreshToken,
            Expiry:       oauthTokenConfig.ExpiresIn,
        }
    
        tokenSource := config.TokenSource(ctx, token)
    
        updatedToken, err := tokenSource.Token()
        if err != nil {
            return nil, err
        }
    
        return &GoogleOauthTokenConfig{
            Config:       config,
            AccessToken:  updatedToken.AccessToken,
            RefreshToken: updatedToken.RefreshToken,
            ExpiresIn:    updatedToken.Expiry,
            TokenType:    updatedToken.TokenType,
        }, nil
    }