Images uploaded to Azure blob storage unavailable when browsing by direct URL - azure

I have uploaded a number of images to a Blob container on an Azure storage account of type StorageV2 (general purpose v2).
These were uploaded programmatically. Here's the code I used:
public Task CopyFile(string fileName, string targetPath)
{
var blobRef = Container.GetBlockBlobReference(targetPath);
blobRef.Properties.ContentType = GetContentType(fileName);
return blobRef.UploadFromFileAsync(fileName);
}
public string GetContentType(string fileName)
{
var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(fileName, out var contentType))
{
contentType = "application/octet-stream";
}
return contentType;
}
Container is an initialized CloudBlobContainer instance.
When I use the Storage Explorer I can see the uploaded files. If I view the properties of any file it lists a Uri property. However, if I copy the value (a URL) and paste into a browser I see the following error page:
<Error>
<Code>ResourceNotFound</Code>
<Message>
The specified resource does not exist. RequestId:12485818-601e-0017-6f69-56c3df000000 Time:2019-08-19T08:35:13.2123849Z
</Message>
</Error>
But if I double-click the file in Storage Explorer it downloads the image correctly. The URL it uses is the same as the one I copied earlier as far as I could tell, except for some additional querystrings that look like this: ?sv=2018-03-28&ss=bqtf&srt=sco&sp=rwdlacup&se=2019-08-19T16:49:38Z&sig=%2FJs7VnGKsjplalKXCcl0XosgUkPWJccg0qdvCSZlDSs%3D&_=1566204636804
I assume this must mean my blobs are not publically available, but I can't find any setting that will make my images available publically at their known URI. Can anyone point me in the right direction here? Thank you.

Check the access level that set to your container.
If that is a Private then you will have the error that you experiencing: ResourceNotFound

As far as I know, if you container's access level is Private, you use the direct url to access the blob then you will get the error. If you want to access it, you need to generate a SAS token for it.
For more details, please refer to
https://learn.microsoft.com/en-us/azure/storage/blobs/storage-manage-access-to-resources
https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview

Related

Check if a file exists in azure blob container

I have an azure blob container name "x" and I want to check if a file name "a.jpg" exists in that blob or not and return true or false based on that. It seems easy but there isn't a clear answer when I google it.
It is clearly mentioned in the document, if you are using c# you can use ExistsAsync method
public async Task<bool> FileExists(string fileName)
{
return await directory.GetBlockBlobReference(fileName).ExistsAsync();
}
You can also use REST Api if you want.
https://learn.microsoft.com/en-us/rest/api/storageservices/get-blob-metadata
A lot more trouble... but works too.
Request The Get Blob Metadata request may be constructed as follows.
HTTPS is recommended. Replace myaccount with the name of your storage
account:
TABLE 1 GET or HEAD Method Request URI HTTP Version
https://myaccount.blob.core.windows.net/mycontainer/myblob?comp=metadata
https://myaccount.blob.core.windows.net/mycontainer/myblob?comp=metadata&snapshot=

Can't implement azure web app service access to azure storage container (blob) using MSI

I have an azure resource group which contains Web App Service and Storage with BLOB container. My web app (.NET Core) tries to retrieve and show an image from container. The container has no public access to content (access level is private). I created system assigned identity for my app and gave it Reader role in storage access control (IAM).
This is how I get access to blobs in app's code:
const string blobName = "https://storagename.blob.core.windows.net/img/Coast.jpg";
string storageAccessToken = await GetStorageAccessTokenAsync();
var tokenCredential = new TokenCredential(storageAccessToken);
var storageCredentials = new StorageCredentials(tokenCredential);
var blob = new CloudBlockBlob(new Uri(blobName), storageCredentials);
ImageBlob = blob.Uri;
GetStorageAccessTokenAsync() does this:
var tokenProvider = new AzureServiceTokenProvider();
return await tokenProvider.GetAccessTokenAsync("https://storage.azure.com/");
Then the image is displayed by
<img src="#Model.ImageBlob" />
I don't get any exceptions in my code, but image from the BLOB container isn't shown with 404 error (specified resource doesn't exist) in browser console.
When I change container's access level to "blob" (public access), app works fine and the image is displayed.
Apparently, it is something wrong with getting credentials part, but I couldn't find any working example nor detailed explanations how it actually should work.
Any help is very appreciated.
UDPATE:
Thank you all who responded. So, it seems I've got two problems here.
1) I don't get credentials properly.
I can see that "AzureServiceTokenProvider" object (Microsoft.Azure.Services.AppAuthentication) that I create, has empty property PrincipalUsed at the runtime.
My application deployed to Azure App Service, which has system managed identity and that identity (service principal) is given permissions in Azure storage (I changed permission from account Reader to Storage Blob Data Reader as was suggested).
Shouldn't it get all data needed from the current context? If not, what I can do here?
2) I use wrong method to show image, but since the app has no access to storage anyway I can't fix it yet.
But still - what is the common way to do that in my case? I mean there is no public access to storage and I use "CloudBlockBlob" to reach images.
Reader gives access to read the control plane, but not the data plane. The role you need is Storage Blob Data Reader, which gives access to read blob contents.
For more details about this, check out: https://learn.microsoft.com/en-us/azure/role-based-access-control/role-definitions#data-operations-example
When you use <img src="#Model.ImageBlob" />, no authorization header is sent in the request by the browser. In your code, you are fetching the token, but the token is not being sent in the authorization header when the image is being fetched. So, storage API thinks this is an anonymous request. This is the reason you are getting a 404.
You need to send auth code when fetching the image. This code works for me
public async Task<ActionResult> Image()
{
const string blobName = "https://storage.blob.core.windows.net/images/image.png";
string storageAccessToken = await GetStorageAccessTokenAsync().ConfigureAwait(false);
var tokenCredential = new TokenCredential(storageAccessToken);
var storageCredentials = new StorageCredentials(tokenCredential);
var blob = new CloudBlockBlob(new Uri(blobName), storageCredentials);
Stream blobStream = blob.OpenRead();
return File(blobStream, blob.Properties.ContentType, "image.png");
}
In the view, I use
<img src="/Home/Image" />
Finally, I got it to work. First of all, the part of code regarding getting token and image from Azure storage was OK. The second problem with displaying image in RazorPages application I resolved, using this code in view:
<form asp-page-handler="GetImage" method="get">
<img src="/MyPageName?handler=GetImage" />
</form>
and corresponding code in model:
public async Task<ActionResult> OnGetGetImageAsync()
{
//getting image code and returning FileContentResult
}
But I'm still thinking: whether is more simple way to do that? Something like to add image collection to the model, fill it using "OnGet..." handler and then display its content using in view. I didn't find a way to use model properties in <img> tag. Does anyone have some suggestions?

How to read .csv file in that located in Azure?

My apologies for asking this basic question. I'm very new in Azure environment.
I have stored log files in Azure portal as .csv
I want to view this .csv file without download it.
Azure already give the URL link for this file. But it is unable to view
This is the link that provides by Azure for my .csv file:
https://xxxxxx.xfile.xcore.windows.net/cctvfeedfs/log/testcsv.csv
Fyi, I do have SAS signature, this SAS signature when I combine with the URL its will download the .csv file. Example like this:
https://xxxxxx.xfile.xcore.windows.net/cctvfeedfs/log/testcsv.csv?sv=2017-11-09&ss=bfqt&srt=sco&sp=rwdlacup&se=2099-10-04T09:06:59Z&st=2018-10-04T01:06:59Z&spr=https&sig=%2Fb%2BrssXtUP5V%2F9%2BSXzpSauyugpG%2BvXOfn9GqLfdf1EOUE%3D
But actually I don't want to download but just want to view it.
It is have any possible way to do so I can view the content in .csv without download it?
Please help. Thank you in advance!
What can I do to view the content online without download it?
If your container is not public,the url can't be viewed the content of the file directly, otherwise there would be no any security for your files.
So please refer to the offical documents Secure access to an application's data in the cloud and Using shared access signatures (SAS). Then, we need to generate a blob url with SAS signature for accessing.
Here is the sample java code to generate a blob url with SAS signature.
SharedKeyCredentials credentials = new SharedKeyCredentials(accountName, accountKey);
ServiceSASSignatureValues values = new ServiceSASSignatureValues()
.withProtocol(SASProtocol.HTTPS_ONLY) // Users MUST use HTTPS (not HTTP).
.withExpiryTime(OffsetDateTime.now().plusDays(2)) // 2 days before expiration.
.withContainerName(containerName)
.withBlobName(blobName);
BlobSASPermission permission = new BlobSASPermission()
.withRead(true)
.withAdd(true)
.withWrite(true);
values.withPermissions(permission.toString());
SASQueryParameters serviceParams = values.generateSASQueryParameters(credentials);
String sasSign = serviceParams.encode();
String blobUrlWithSAS = String.format(Locale.ROOT, "https://%s.blob.core.windows.net/%s/%s%s",
accountName, containerName, blobName, sasSign);
You also can add the SAS signature at the end of the string of blob.toURL().
String blobUrlWithSAS = blob.toString()+sasSign;
About SAS Signature, you can refer to these sample codes in ServiceSASSignatureValues Class and AccountSASSignatureValues Class.
You could check the ContentType with your csv file in Azure Storage Explorer Tool.
If you change the format of it to text/plain,
then it could show the content directly in the browser.
BTW,you could set the content type when you upload the file.(Please see this SO case :Uploading blockblob and setting contenttype)

How to add Authorization header to SAS URI?

I am working on a POC where I have to create a simulated device and connected to IOT HUB , this part is done after this some external application sends message to IOT HUB for that device.
Message contains the blob storage SAS URI, this same file I need to download to device.
Simulated device able to get the SAS URI and but when I am start downloading the file below error I am getting.
Exception in thread "main" com.microsoft.azure.storage.StorageException: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
Rectify me if my approach is wrong and correct me with appropriate approch for this use case.
private static void download(String message) throws StorageException, IOException, JSONException, URISyntaxException {
// need to download the file to simulator in folder
try {
JSONObject jsonObject = new JSONObject(message);
String sasUri = (String) jsonObject.get("fileUrl");
System.out.println("SAS URI from hub ->" + sasUri + " ");
URI url = new URI(sasUri);
//downloadFile(sasUri);
System.out.println("end of file download function");
CloudBlob blob = new CloudBlockBlob(url);
blob.downloadToFile("/path/to/download/file");
} catch(Exception e) {
e.printStackTrace();
}
}
Below is SAS URI :-
https://*******.blob.core.windows.net/test/testfile.zip?sv=2017-07-29&ss=b&srt=sco&sp=rwdlac&se=2018-04-16T13:33:22Z&st=2018-04-16T05:00:22Z&spr=https&sig=***********
I am getting the SAS URI from azure portal directly , not generating at runtime.
Thanks in advance!
To narrow down the issue you can have a try of the following method to see if it helps.
Get the URI of the blob in Azure Portal by click "Download" like this:
After that, the file will be downloaded. You can find the URI in explorer download history. The URI format will like this:
https://[storage-account].blob.core.windows.net/testdownload/20180417_4.zip?sv=2017-07-29&ss=bqtf&srt=sco&sp=rwdlacup&se=2018-04-19T15:52:15Z&sig=[signature]
Directly use this URI in the following code piece and it will work.
CloudBlob blob = new CloudBlockBlob(url);
await blob.DownloadToFileAsync(imgPath, System.IO.FileMode.CreateNew);
Upate: Another way to get absolute URI to the blob from Azure Portal looks like this:
First get SAS token. Note the Start and expiry date/time. The token is valid only in this time period.
Second get blob URL.
Finally, the complete absolute URI to the blob is blob URL plus SAS token. It will like this:
https://ritastorageaccount.blob.core.windows.net/?sv=2017-07-29&ss=b&srt=sco&sp=rwdlac&se=2018-04-26T10:01:47Z&st=2018-04-26T02:01:47Z&spr=https&sig=[SIG]

Azure blob, controlling filename on download

Our application enables users to upload files via a web browser. The file name gets mapped to a GUID and the blob name becomes the GUID. When the user clicks the file in our application it should download to their file system (show save as, etc) using the original file name and not the GUID blob name.
I found this post, and similar posts that describe how to set the Content-Disposition on the blob when downloading through a Shared Access Signature.
Friendly filename when public download Azure blob
However, our situation is a little different. We set a single SAS at the Container level (technically this is called a Shared Access Policy I believe -- you can have up to 5 at any given time). When downloading the blob, we simply append the SAS to the end of the uri, and use...
window.location.href = blobUri + containerSAS;
...to download the blob. This downloads the blob, but uses the GUID filename.
How can we take an existing SAS that applies to the Container and have the blob download as the original filename?
Keep in mind this is a slightly different use case from a SAS applied to an individual blob in that...
The SAS is at the Container level (it seems this is the best practice vs. individual SAS's for each blob).
We are downloading from javascript (vs. C# code where you can set the headers).
I have tried to set the Content-Disposition of the blob during the upload process (PUT operation), but it doesn't seem to make a difference when I download the blob. Below, you can see the Content-Disposition header being set for the PUT request (from Fiddler).
Thanks!
This post pointed us in the right direction to change the file name with the the ContentDisposition property Azure.Storage.Blobs
BlobContainerClient container = OpenContianer(containerName);
BlobClient blob = container.GetBlobClient(sourceFilename);
var Builder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.Now.AddMinutes(10));
Builder.ContentDisposition= $"attachment; filename = {destFileName} ";
var SasUri = blob.GenerateSasUri(Builder);
I have a solution. I think it's more of a workaround, but for each file to be downloaded, I make a server call and create a special SAS for the download operation. I can set Content-Disposition with that, and now the GUID named blobs are downloading with their original filenames.

Resources