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:
go1.21.1 darwin/arm64
EDIT:
I have also replicated the official example from Google and it still doesn't work.
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
}