无法通过Spring Boot REST API上传对象至GCP Storage的问题
问题背景
使用Spring Boot REST API上传文件至GCP Storage时触发403错误,提示存储桶对应的账户未启用计费,但已确认该账户计费功能处于开启状态。此外,用Express和Python Flask的REST API可正常上传至同一存储桶,且此前用相同Spring Boot代码实现过该功能。
错误信息
com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
POST https://storage.googleapis.com/upload/storage/v1/b/pdf-split-ingestion/o?projection=full&uploadType=multipart{ "code" : 403, "errors" : [ { "domain" : "global", "location" : "Authorization", "locationType" : "header", "message" : "The account for bucket \"pdf-split-ingestion\" has not enabled billing.", "reason" : "accountDisabled" } ], "message" : "The account for bucket \"pdf-split-ingestion\" has not enabled billing." }
相关代码
@RestController @RequestMapping("") public class Controller { @Autowired private Repo repo; String projectId = "spring-b-357511"; String bucketName = "pdf-split-ingestion"; Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService(); @PostMapping("/upload-file") public ResponseEntity<String> uploadFile1(@RequestParam("file")MultipartFile file){ String Name = file.getOriginalFilename(); if (file.isEmpty()){ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("File is missing in request"); } try{ InputStream is = file.getInputStream(); byte data[] = new byte[is.available()]; is.read(data); BlobId blobId = BlobId.of(bucketName, file.getOriginalFilename()); BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build(); storage.create(blobInfo,data); } catch (IOException e) { e.printStackTrace(); } Model response = new Model(); repo.save(response); return ResponseEntity.ok("File uploaded Successfully"); }
解决方案
验证应用使用的身份凭据
你的代码未显式指定服务账号密钥,默认使用运行环境的默认凭据:- 本地运行:检查
gcloud auth application-default login关联的账号是否为已开计费的正确账号;或确认GOOGLE_APPLICATION_CREDENTIALS环境变量指向的服务账号密钥,所属项目已开计费且拥有存储桶写入权限。 - GCP环境运行:检查实例/服务绑定的服务账号是否属于目标项目,是否拥有
roles/storage.objectCreator及以上权限,同时确认项目计费状态正常。
- 本地运行:检查
确认存储桶与项目的关联
检查存储桶pdf-split-ingestion是否确实属于项目spring-b-357511,避免因项目ID错误,请求到了未开计费的其他项目下的存储桶,可通过GCP控制台存储桶详情页查看所属项目。检查凭据权限范围
确保使用的账号拥有足够权限:至少需要storage.objects.create权限,可通过绑定Storage Object Creator角色实现;同时确认没有针对该账号设置计费配额限制或IAM拒绝规则。升级GCP Storage客户端库
旧版本客户端库可能存在计费校验逻辑问题,尝试升级google-cloud-storage依赖到最新稳定版,例如在pom.xml中更新:<dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-storage</artifactId> <version>2.22.0</version> <!-- 替换为最新版本 --> </dependency>显式指定服务账号密钥
为排除默认凭据的问题,可在代码中显式指定服务账号密钥路径,确保使用正确账号:Storage storage = StorageOptions.newBuilder() .setProjectId(projectId) .setCredentials(ServiceAccountCredentials.fromStream(new FileInputStream("/path/to/your/service-account-key.json"))) .build() .getService();
内容的提问来源于stack exchange,提问作者Aravinth kumar

