I'm uploading a file to Google Storage and try to generate a public link:
Storage.BlobWriteOption precondition;
Blob blob = storage.get(bucketName, name);
if (blob == null) {
precondition = Storage.BlobWriteOption.doesNotExist();
} else {
precondition = Storage.BlobWriteOption.generationMatch(blob.getGeneration());
}
Blob rs = storage.createFrom(BlobInfo.newBuilder(BlobId.of(bucket.getName(), name)).setContentType("application/octet-stream").build(), inputStream, precondition)
BlobId blobId = rs.getBlobId();
URL url =
storage.signUrl(
BlobInfo.newBuilder(blobId).build(),
Duration.ofHours(1).toMillis() - 10000,
TimeUnit.MILLISECONDS,
Storage.SignUrlOption.httpMethod(HttpMethod.GET),
Storage.SignUrlOption.withExtHeaders(Map.of("Content-Type", "application/octet-stream")));
I got a link like https://storage.googleapis.com/vocab-be-local/vocab.zip?GoogleAccessId=my-account@vocab-local.iam.gserviceaccount.com&Expires=1715751607&Signature=Vz2G8qr%2Fy%2BN8vR7yiPIQlnpzyaz2s5qGLcTL4nE7w3B6gXprtODGzz8Dk49v%2BMbrCg6IcHi1ZGN9ZNYVZyM7VW3RUZ6QOOIPbA62AEzM1T9UUYIaxGYkE7xGCFC3a6LBv%2FCnN6F28eau3wdGpldwC5iwccrwSYIpozK43Whklk%2B%2BHekjq%2BgnI%2F%2FnuqBWa1Z9P8RBejlz9ajnaxriaFOUyvlBLQBShnxM4q%2BdAJWeFlocMZIk7ZSiGuh1gTi2HTecxzod9bIRu%2FmhecpMlmPP2PvQSW0dqxMuonfxxxj3Eby7hRXOy6wNKSWU4DqZkUJm29AHOyO1vqgc2KaQevbWdw%3D%3D
When I use this link via Postman or curl, I got:
<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>Access denied.</Message>
<Details>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Details>
<StringToSign>GET
application/octet-stream
1715751607
/vocab-be-local/vocab.zip</StringToSign>
</Error>
Tried with Webflux WebClient, still got same error:
WebClient.create()
.get().uri(d.getImageUrl())
.header("Content-Type", "application/octet-stream")
.retrieve()
.toEntity(byte[].class)
.block()
I have read a lot of questions/answers related to this issue. They suggest that I need to add content-type when uploading/signing. I did it, but no luck. Please help!!
UPDATE:
Spring boot 3.2.0
Spring cloud 2023.0.1
Spring cloud GCP 5.1.2
My pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-storage</artifactId>
</dependency>
My application.yml:
spring:
cloud:
gcp:
credentials:
location: classpath:google/sa/vocab-storage-local.json
storage:
project-id: my-project
My service account JSON:
{
"type": "service_account",
"project_id": "my-project",
"private_key_id": "***********",
"private_key": "-----BEGIN PRIVATE KEY-----\n********\n-----END PRIVATE KEY-----\n",
"client_email": "******@******.iam.gserviceaccount.com",
"client_id": "*******************",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/********",
"universe_domain": "googleapis.com"
}
Fixed:
storage.signUrl(BlobInfo.newBuilder(blob.getBlobId())
-> Add this line .setContentType("application/octet-stream")
.build(),
32515291723000L - currentTimeMillis(),
TimeUnit.MILLISECONDS,
Storage.SignUrlOption.httpMethod(HttpMethod.GET),
-> Fix this line Storage.SignUrlOption.withContentType()))