使用Go生成GCP presignURL上传图片时遇SignatureDoesNotMatch错误
GCS预签名URL上传出现SignatureDoesNotMatch错误的解决办法
问题场景
我用Go代码通过预签名URL上传图片到Google Cloud Storage(GCS)时,持续收到SignatureDoesNotMatch的403错误。已经给目标存储桶配置了拥有Storage Admin和Storage Object Admin权限的服务账号,下载了对应的JSON密钥文件,生成预签名URL时明确指定了Content-Type: image/jpeg。
错误原因
排查后发现,上传请求中使用了multipart.NewWriter来构建请求体,这导致实际发送的请求头里Content-Type被自动设置为multipart/form-data,和生成预签名URL时指定的image/jpeg不一致——GCS的预签名URL验证要求请求的关键参数(包括Content-Type)必须和签名时完全匹配,参数不符就会触发签名验证失败,返回403错误。
解决方案
修改上传代码,直接以原始文件的image/jpeg类型发送请求,不再使用multipart格式,确保请求的Content-Type和生成预签名URL时的参数一致。
修正后的代码示例
生成预签名URL的代码
package main import ( "context" "fmt" "time" "cloud.google.com/go/storage" "google.golang.org/api/option" ) func generatePresignedURL(bucket, object string) (string, error) { ctx := context.Background() client, err := storage.NewClient(ctx, option.WithCredentialsFile("service-account-key.json")) if err != nil { return "", fmt.Errorf("storage.NewClient: %v", err) } defer client.Close() opts := &storage.SignedURLOptions{ GoogleAccessID: "your-service-account@your-project.iam.gserviceaccount.com", PrivateKey: []byte("your-private-key"), // 可从密钥文件中读取 Method: "PUT", Expires: time.Now().Add(15 * time.Minute), ContentType: "image/jpeg", // 明确指定与上传一致的Content-Type } url, err := client.Bucket(bucket).SignedURL(object, opts) if err != nil { return "", fmt.Errorf("Bucket(%q).SignedURL: %v", bucket, err) } return url, nil }
上传文件的代码
package main import ( "fmt" "io" "net/http" "os" ) func uploadFile(presignedURL, filePath string) error { file, err := os.Open(filePath) if err != nil { return err } defer file.Close() req, err := http.NewRequest("PUT", presignedURL, file) if err != nil { return err } req.Header.Set("Content-Type", "image/jpeg") // 严格匹配签名时的Content-Type client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("upload failed: %s", resp.Status) } return nil }
内容的提问来源于stack exchange,提问作者Jerome
相关产品推荐
相关产品推荐

