1

I've done a lot of research, and lots of tests before asking this question, as usual, I don't like asking just because.

I can't make a signed url using java that displays the file (content-disposition:inline) in the browser.

When I do it using the PHP sdk, I have no trouble, I copy and paste the signed url in the browser and the file is displayed in the browser. But when using java for the same file, the file is downloaded.

In this post, it mentions something about clear metadata and appending "&response-content-disposition=inline", to the signed url, it is also mentioned in the google cloud docs.

I have tried in several ways, but I just can't make it work, when I append the "&response-content-disposition=inline" as specified in the docs, I get the following error:

 <Code>SignatureDoesNotMatch</Code> 
 <Message> The request signature we
 calculated does not match the signature you provided. Check your
 Google secret key and signing method. </Message>

But it says that those parameters aren't included in the calculation of the signing, so I don't know what's happening.

In code I've tried:

    (transformed to kotlin)

    val blobInfo = BlobInfo.newBuilder(BlobId.of(configuracion.bucket, fileName)).build()
    val newMetadata: MutableMap<String, String> = HashMap()
    newMetadata["contentDisposition"] = "inline"
    blobInfo.toBuilder().setMetadata(newMetadata).build()

    val signUrl = storage.signUrl(blobInfo, expiration, TimeUnit.MILLISECONDS,Storage.SignUrlOption.withV4Signature())
        
    return signUrl.toString()

But no luck.

I would think that there should be a pretty straight forward way to set content-disposition to whatever I want, but it doesn't seem to be the case.

The way I generate the signed url is simple:

val storage = this.getStorageDefaultInstance()

val blobInfo = BlobInfo.newBuilder(BlobId.of(configuracion.bucket, fileName)).build()

val signUrl = storage.signUrl(blobInfo, expiration, TimeUnit.MILLISECONDS, Storage.SignUrlOption.withV4Signature())

return signUrl.toString()

Any suggestions?

EDIT

I noticed that when I request the file using PHP SDK I get the reponse header "content-type: application/pdf", while on Java I get "content-type: application/octet-stream", maybe this is what I need to change.

I tried overwriting metada using:

    val newMetadata: MutableMap<String, String> = HashMap()
    newMetadata["contentDisposition"] = "inline"
    newMetadata["contentType"] = "application%2Fpdf"
    val blobInfo = BlobInfo.newBuilder(BlobId.of(configuracion.bucket, fileName)).setMetadata(newMetadata).build()

Still, no luck.

Lauro182
  • 1,597
  • 3
  • 15
  • 41

2 Answers2

4

So,

I kept reading and fiddling with the code on the documentation, until I figured it out.

All the reading and research started making more sense the more I read.

So if anyone has the same problem in the future, the code I finally used to change the response headers is:

val storage = this.getStorageDefaultInstance()

val blobInfo = BlobInfo.newBuilder(BlobId.of(configuracion.bucket, fileName)).build()

//The query params
val queryParams: MutableMap<String, String> = HashMap()
queryParams["response-content-disposition"] = "inline"
queryParams["response-content-type"] = "application/pdf"

val signUrl = storage.signUrl(blobInfo, expiration, TimeUnit.MILLISECONDS, 
Storage.SignUrlOption.withQueryParams(queryParams), //This is the magic line
Storage.SignUrlOption.withV4Signature())

return signUrl.toString()

To make it clearer, when I was reading other posts and documentation, about appending to the url the params (response-content-disposition and response-content-type), I tried serveral ways, somewhere in a comment I read response-content-disposition alone wasn't enough, and this was the case for me, I also needed to add response-content-type too, but my main mistake was appending these after the url was signed.

You need to create the url and append the params to the query string, prior to sign the url (example code above), and the google api will return the signed url including both params appended in the query string with the values specified, so you don't need to modify the url afterwards, it is ready to be used.

Hope this helps other people save time.

Lauro182
  • 1,597
  • 3
  • 15
  • 41
0

Here is the code I ran, this generates the signed url for pdf that is then rendered in the browser, if this doesn't work for you, I'd look into pdf or browser settings.

String projectId  = "<project_name>";
String bucketName = "<bucket_name>";
String objectName = "<file_name>";

Storage strg = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
BlobInfo blobinfo = BlobInfo.newBuilder(BlobId.of(bucketName, objectName)).build();
FileInputStream key = new FileInputStream("<path_to_json>");

URL url = strg.signUrl(blobinfo, 
  1, TimeUnit.MINUTES, Storage.SignUrlOption.withV4Signature(),
  SignUrlOption.signWith(ServiceAccountCredentials.fromStream(key))
);

System.out.println("Signed URL:");
System.out.println(url);
jabbson
  • 4,390
  • 1
  • 13
  • 23
  • The thing is, when I request same file through PHP it opens in the browser, no problem. When I do it on java, it downloads, I noticed the header content-type its different, I'm gonna add an edit on question. – Lauro182 Mar 29 '21 at 19:41