I am trying to download an image (.jpg) from an Azure storage blob using:
public static async Task DownloadToTemp(string path, string fileName)
{
string storageAccount_connectionString = "CONNECTION STRING";
CloudStorageAccount mycloudStorageAccount = CloudStorageAccount.Parse(storageAccount_connectionString);
CloudBlobClient blobClient = mycloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(CONTAINER);
CloudBlockBlob cloudBlockBlob = container.GetBlockBlobReference(fileName);
// provide the file download location below
Stream file = File.OpenWrite(path);
await cloudBlockBlob.DownloadToStreamAsync(file);
file.Close();
return;
}
But when I try to open the image as a bit map Bitmap image = new Bitmap(path) I am getting the error System.ArgumentException: 'Parameter is not valid.'. I am using the call BlobHandler.DownloadToTemp(path, file).GetAwaiter() to ensure the file has been downloaded.
So the problem lies with not waiting fully for the image to be stored:
adding:
await BlobHandler.DownloadToTemp(path, file);
Ensures that the file is fully downloaded (This meant that the callerfunction had to be async).
Related
I am trying to read a blob from Azure using C# Console application. I can download the blob file to my local machine and read it but when I am trying to download it to memory stream it is throwing an error as attached.
I want to directly read my blob file instead of downloading it to local machine as I have to deploy it in Azure. Any suggestions on it?
var storageaccount = new CloudStorageAccount(new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(StorageAccountName, StorageAccountKey), true);
var blobclient = storageaccount.CreateCloudBlobClient();
var container = blobclient.GetContainerReference("");
// var blobs = container.ListBlobs();
var blockBlobReference = container.GetBlockBlobReference(sourceBlobFileName);
using (var memorystream = new MemoryStream())
{
blockBlobReference.DownloadToStream(memorystream);
}
I think the problem is because you're wrapping it in a using statement. Try the following:
var storageaccount = new CloudStorageAccount(new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(StorageAccountName, StorageAccountKey), true);
var blobclient = storageaccount.CreateCloudBlobClient();
var container = blobclient.GetContainerReference("");
// var blobs = container.ListBlobs();
var blockBlobReference = container.GetBlockBlobReference(sourceBlobFileName);
var memorystream = new MemoryStream();
blockBlobReference.DownloadToStream(memorystream);
byte[] content = memoryStream.ToArray();
I tried your code, but Blob downloaded successfully.
your screenshot just shows the variable in Visual Studio debugger, and it looks like the error doesn't actually occurred.
In screenshot, MemoryStream, CanTimeout property set to false.
So, it seems that ReadTimeout and WriteTimeout throw exceptions only because MemoryStream does not support them.
Reference:
Stream.ReadTimeout Property
Please note that the exception won't occurred until you use ReadTimeout and WriteTimeout properties.
First of all, i am new in azure development, so my question will be ambiguous. i need better suggestion.
I have web api C# that receive binary data (image). I want to write it into azure blob storage. In my case, i need to check if blob storage exist then write the image else create the directory and write the image.
This directory is public read access so that i can reflect image in any public app.
Following method store the image and return the image public path
public class BlobStorageService : IBlobStorageService
{
public async Task<string> SaveSingleImage(byte[] image)
{
throw new NotImplementedException();
}
}
I have done it. Following is my code. This will take image byte and save into blob storage and return the public URL of that image
public async Task<string> GoAppSaveSingleImage(ProofImageViewModel proofImage)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(GlobalConfig.Instance.AzureBlobStorageForGoAppImages);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(GlobalConfig.Instance.AzureBlobGoAppImagesContainerName);
CloudBlockBlob blockBlob = container.GetBlockBlobReference($"{proofImage.CountryCode}/{DateTime.Now.Year}/{DateTime.Now.Month}/{proofImage.ProofImage}");
blockBlob.Properties.ContentType = "image/png";
if (GlobalConfig.Instance.AzureBlobGoAppImagesIsStorageTier)
{
blockBlob.SetStandardBlobTierAsync((StandardBlobTier)GlobalConfig.Instance.AzureBlobGoAppImagesStorageTier).GetAwaiter().GetResult();
}
using (var stream = new MemoryStream(proofImage.ProofImage, writable: false))
{
await blockBlob.UploadFromStreamAsync(stream);
}
string publicUrl = blockBlob.Uri.AbsoluteUri;
return publicUrl;
}
I'm using the ImageSharp library to rescale my images before they are uploaded to Azure, the application hangs with no errors when it reaches the UploadBlob action and I think it's the stream causing it. When the image is uploaded the information is gathered from the image stream, I create an empty MemoryStream, resize the image using ImageSharp, stuff the MemoryStream with my newly scaled image and try to upload that MemoryStream to Azure and I don't think it likes it as that is where it hangs.
Is MemoryStream the correct thing to use in this instance or is it something else?
CarController.cs
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Car car)
{
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
if (ModelState.IsValid)
{
//Access the car record
_carService.InsertCar(car);
//Get the newly created ID
int id = car.Id;
//Give it a name with some virtual directories within the container
string fileName = "car/" + id + "/car-image.jpg";
string strContainerName = "uploads";
//I create a memory stream ready for the rescaled image, not sure this is right.
Stream outStream = new MemoryStream();
//Access my storage account
BlobServiceClient blobServiceClient = new BlobServiceClient(accessKey);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(strContainerName);
//Open the image read stream
var carImage = car.ImageFile.OpenReadStream();
//Rescale the image, save as jpeg.
using (Image image = Image.Load(carImage))
{
int width = 250;
int height = 0;
image.Mutate(x => x.Resize(width, height));
image.SaveAsJpeg(outStream);
}
var blobs = containerClient.UploadBlob(fileName, outStream);
return RedirectToAction(nameof(Index));
}
return View(car);
}
It's not got anything to do with the ImageSharp library.
You need to reset your outStream position after saving. BlobContainerClient is trying to read from the end of the stream.
I am trying to get my MVC controller to return a PDF stored in an Azure Blob Container. The client's browser will download the PDF fine, but when they open it they will see "Error Failed to load PDF document." when opening it in Chrome (though the file does not open in other browsers either).
I was able to download the file on my machine and open it fine doing the following:
public static void DownloadFile()
{
CloudBlockBlob cloudBlockBlob =
CloudBlobContainer.GetBlockBlobReference("document.pdf");
AsyncCallback callback = new AsyncCallback(DownloadComplete);
cloudBlockBlob.BeginDownloadToFile(#"path\document.pdf", FileMode.Create,
callback, new object());
}
However I would rather not create any temp files on the server and return those. I would like to just create them in memory and return that to the client.
My Controller Code:
public async Task<FileStreamResult> Test()
{
MemoryStream stream = await BlobStorageUtils.DownloadFile();
return File(stream, "application/pdf", "document.pdf");
}
The code to retrieve the file from the Blob Container
public static async Task<MemoryStream> DownloadFile()
{
CloudBlockBlob cloudBlockBlob =
CloudBlobContainer.GetBlockBlobReference("document.pdf");
MemoryStream stream = new MemoryStream();
await cloudBlockBlob.DownloadToStreamAsync(stream);
return stream;
}
As I mentioned, my file downloads in the browser fine, but I receive the error when trying to open the file.
Eventually I would like this to work with any type of document, not just PDFs.
Edit: I should note that I have tried this with image files as well (PNG specifically) and had a similar issue where the image was corrupted or couldn't be opened. The error I received then was "It looks like we don't support this file format".
Update: See my solution below for how I got this to work.
The solution for this came from this link: Open a pdf file(using bytes) stored in Azure Blob storage
I ended up just dumping the byte stream from Azure into the response's output stream. However you need to make sure that the content type of the response is set to "application/pdf". My code ended up like this:
Controller Code:
public async Task<ActionResult> Test()
{
Response.Buffer = true;
Response.Clear();
Response.ContentType = "application/pdf";
await BlobStorageUtils.DownloadFile(Response.OutputStream);
return new EmptyResult();
}
Code to retrieve file from Blob Container
public static async Task DownloadFile(Stream outputStream)
{
CloudBlockBlob cloudBlockBlob =
CloudBlobContainer.GetBlockBlobReference("document.pdf");
await cloudBlockBlob.DownloadToStreamAsync(outputStream);
}
I'm downloading file from blob container & saving in to a stream & trying to read pdf.
//creating a Cloud Storage instance
CloudStorageAccount StorageAccount = CloudStorageAccount.Parse(connectionstring);
//Creating a Client to operate on blob
CloudBlobClient blobClient = StorageAccount.CreateCloudBlobClient();
// fetching the container based on name
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
//Get a reference to a blob within the container.
CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
var memStream = new MemoryStream();
blob.DownloadToStream(memStream);
try
{
PdfReader reader = new PdfReader(memStream);
}
catch(Exception ex)
{
}
Exception : PDF header signature not found.
The reason, as was evident after troubleshooting through the comments was that this line:
blob.DownloadToStream(memStream);
positioned the stream right after the downloaded content.
Then, when constructing the pdf reader object it expected to find the Pdf file form the current position.
This is a common problem when dealing with streams that one first write something to and then afterwards tries to read that something, one must remember to reposition the stream if necessary.
In this case, assuming there is only the pdf in the stream, the solution is to reposition the stream back to the start before attempting to read the pdf file:
Add this line:
memStream.Position = 0;
after the download but before the reader is constructed to reposition.
Here's what the code can look like in this region:
blob.DownloadToStream(memStream);
memStream.Position = 0; // <----------------------------------- add this line
try
{
PdfReader reader = new PdfReader(memStream);