Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I view an image from Azure Blob Storage, rather than download it?

Ok, so I am using Node.js and Azure Blob Storage to handle some file uploads.

When a person uploads an image I would like to show them a thumbnail of the image. The upload works great and I have it stored in my blob.

I used this fine link (Generating Azure Shared Access Signatures with BlobService.getBlobURL() in Azure SDK for Node.js) to help me create this code to create a share access temporary url.

process.env['AZURE_STORAGE_ACCOUNT'] = "[MY_ACCOUNT_NAME]";
process.env['AZURE_STORAGE_ACCESS_KEY'] = "[MY_ACCESS_KEY]";

var azure = require('azure');
var blobs = azure.createBlobService();

var tempUrl = blobs.getBlobUrl('[CONTAINER_NAME]', "[BLOB_NAME]",  { AccessPolicy: {
    Start: Date.now(),
    Expiry: azure.date.minutesFromNow(60),
    Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.READ
}});

This creates a url just fine.

Something like this : https://[ACCOUNT_NAME].blob.core.windows.net:443/[CONTAINER_NAME]/[BLOB_NAME]?st=2013-12-13T17%3A33%3A40Z&se=2013-12-13T18%3A33%3A40Z&sr=b&sp=r&sig=Tplf5%2Bg%2FsDQpRafrtVZ7j0X31wPgZShlwjq2TX22mYM%3D

The problem is that when I take the temp url and plug it into my browser it will only download the image rather than view it (in this case it is a simple jpg file).

This translates to my code that I can't seem to view it in an tag...

The link is right and downloads the right file...

Is there something I need to do to view the image rather than download it?

Thanks, David

UPDATE

Ok, so I found this article: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/b8759195-f490-420b-a587-2bb614366ad2/embedding-images-from-blob-storage-in-ssrs-report-does-not-work

Basically it told me that wasn't setting the file type when uploading it so the browser didn't know what to do with it.

I used code from here: http://www.snip2code.com/Snippet/8974/NodeJS-Photo-Upload-with-Azure-Storage/

This allowed me to upload it correctly and it now views properly in the browser.

The issue I am having now is that when I put the tempUrl into an img tag I get this error:

Failed to load resource: the server responded with a status of 403 (Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.)

This is the exact same link that if I post it to my browser it works just fine...why can't I show it in an image tag?

UPDATE 2

Ok, so as a stupid test I put in a 7 second delay from when my page loads and when the img tag gets the source from the temp url. This seems to fix the problem (most of the time), but it is, obviously, a crappy solution even when it works...

At least this verifies that, because it works sometimes, my markup is at least correct.

I can't, for the life of me, figure out why a delay would make a bit of difference...

Thoughts?

UPDATE 3

Ok, based on a comment below, I have tried to set my start time to about 20 minutes in the past.

var start = moment().add(-20, 'm').format('ddd MMM DD YYYY HH:mm:ss');
var tempUrl = blobs.getBlobUrl(Container, Filename,  { AccessPolicy: {
    Start: start,
    Expiry: azure.date.minutesFromNow(60),
    Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.READ
}});

I made my start variable the same format as the azure.date.minutesFromNow. It looks like this: Fri Dec 13 2013 14:53:58

When I do this I am not able to see the image even from the browser, much less the img tag. Not sure if I am doing something wrong there...

UPDATE 4 - ANSWERED

Thanks to the glorious @MikeWo, I have the solution. The correct code is below:

var tempUrl = blobs.getBlobUrl('[CONTAINER_NAME]', "[BLOB_NAME]",  { AccessPolicy: {
    Start: azure.date.minutesFromNow(-5),
    Expiry: azure.date.minutesFromNow(45),
    Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.READ
}});

Mike was correct in that there seemed to be some sort of disconnect between the start time of the server and my localhost so I needed to set the start time in the past. In update 3 I was doing that, but Mike noticed that Azure does not allow the start and end time to be more than 60 minutes...so in update 3 I was doing -20 start and 60 end which is 80 minutes.

The new, successful way I have above makes the total difference 50 minutes and it works without any delay at all.

Thanks for taking the time Mike!

like image 212
David Avatar asked Dec 13 '13 17:12

David


1 Answers

Short version: There is a bit of time drift that occurs in distributed systems, including in Azure. In the code that creates the SAS instead of doing a start time of Date.now(), set the start time to a minute or two in the past. Then you should be able to remove the delay.

Long version: The clock on the machine creating the signature and adding the Date.now might be a few seconds faster than the machines in BLOB storage. When the request to the URL is made immediately the BLOB service hasn't hit the "start time" yet of the BLOB and thus throws the 403. So, by setting the start time a few seconds in the past, or even the start of the current day if you want to cover a massive clock drift, you building in handling of the clock drift.

UPDATE: After some trial and error: Make sure that when creating an adhoc SAS it can't be longer than an hour. Setting the start time a few minutes in the past and then expiration 60 minutes in the future was too big. Making it a little in the past and then not quite an hour from then for expiration.

like image 166
MikeWo Avatar answered Oct 11 '22 16:10

MikeWo