Hoser
Hoser

Reputation: 5044

Cloud Storage SignedUrl with Different Filename Download in .Net

I'm successfully creating a signed download url using the UrlSigner class provided by the .NET SDKs for Cloud Storage. I'm on Google.Cloud.Storage.V1 3.5.0.

What I'm trying to do now is have that url download the file not as the file key as it is in the bucket, but as a user friendly string.

From my understanding this needs to be done with the Content-Disposition header. Here is my code currently:

var contentDispositionQueryParam = new KeyValuePair<string, IEnumerable<string>>(
    "Content-Disposition", new[] { $"attachment; filename={fileInfo.CustomerFileName}" });
                    
var requestTemplate = UrlSigner.RequestTemplate
    .FromBucket(_cloudStorageBucketName)
    .WithObjectName(fileInfo.FileKey)
    .WithHttpMethod(HttpMethod.Get)
    .WithQueryParameters(new []{contentDispositionQueryParam});
            
var options = UrlSigner.Options.FromDuration(TimeSpan.FromMilliseconds(_urlExpirationTimeMs));
    preSignedUrl.Url = await _objectUrlSigner.GetSignedUrlAsync(requestTemplate, options, token);

This unfortunately is not working. The url generated has no reference to this query param so it feels like it's being filtered out somewhere along the way. Is this an invalid value? I also tried response-content-disposition with no luck.

I was basing my assumption off of this here - https://cloud.google.com/storage/docs/xml-api/reference-headers#contentdisposition and other pieces of documentation for how to send query params, but there really isn't a clear example of how to do this that I can find.

I have seen examples where we update the Content-Disposition metadata of the uploaded file but for various reasons this will not work for me.

EDIT for clarity:

This is how I tried using response-content-disposition

Same setup as above except:

var contentDispositionQueryParam = new KeyValuePair<string, IEnumerable<string>>(                        
    "response-content-disposition", new[] { HttpUtility.UrlEncode($"attachment; filename={fileInfo.CustomerFileName}") });

I'm basing my usage of this off the documentation here - https://cloud.google.com/storage/docs/xml-api/reference-headers#responsecontentdisposition

Upvotes: 0

Views: 177

Answers (1)

Hoser
Hoser

Reputation: 5044

I got this sorted eventually. It doesn't help that for about 12 hours I was putting my changes in the wrong method so my code was never called, but either way here is my final solution:

var contentDispositionQueryParam = new Dictionary<string, IEnumerable<string>>
{
    {
        "response-content-disposition", new List<string>
        {
            $"attachment; filename=\"{fileName}\""
        }
    }
};
                    
var requestTemplate = UrlSigner.RequestTemplate
    .FromBucket(_cloudStorageBucketName)
    .WithObjectName(fileKey)
    .WithHttpMethod(HttpMethod.Get)
    .WithQueryParameters(contentDispositionQueryParam);

var options = UrlSigner.Options.FromDuration(TimeSpan.FromMilliseconds(_urlExpirationTimeMs));
signedUrl = await _objectUrlSigner.GetSignedUrlAsync(requestTemplate, options, token);

This does not work if you url encode the query param value yourself or if you do not include the double quotes around your desired file name, so just be careful with those things.

Upvotes: 1

Related Questions