Upload file into Blob Azure - c#

I'm trying to upload files into blob Azure from an MVC app, I'm following this tutorial, and I'm stuck in this snippet:
using (var fileStream = System.IO.File.OpenRead(#"path\myfile"))
{
blockBlob.UploadFromStream(fileStream);
}
How to make #"path\myfile" dynamic?? How to retrieve the real path of my file to put it in there? I accept any other suggestion too to upload to blob :)
UPDATE
As suggested I added the code inside my View:
#model RiPSShared.Models.RiPSModels.AgencyNote
<h2>PostFile</h2>
<form action="#Url.Action("PostFile", "AgencyNotes", new { NoteId=Model.aut_id})" method = "post" enctype="multipart/form-data">
<label for="file1"> File name:</label>
<input type="file" name="file" id="file1" />
<input type="submit" value="Submit" />
</form>
And the full action controller:
public ActionResult PostFile(HttpPostedFileBase file, int NoteId)
{
CloudBlockBlob blockBlob = container.GetBlockBlobReference(path);
using (Stream fileStream = file.InputStream)
{
blockBlob.UploadFromStream(fileStream);
}
return RedirectToAction("Index");
}

HttpPostedFileBase will have the information you need. Specifically the InputStream property will give you the stream
[HttpPost]
public ActionResult PostFile(HttpPostedFileBase file, int NoteId)
{
// Your existing code for azure blob access
CloudBlockBlob blockBlob = container.GetBlockBlobReference("myblob");
// make sure to a null check on file parameter before accessing the InputStream
using (var s = file.InputStream)
{
blockBlob.UploadFromStream(s);
}
// to do :Return something
}

Also you set your connection string in configuration file and get from that and store file into blob storage first get its container name
//StorageConnectionString string specified in configuration file
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
CloudConfigurationManager.GetSetting("StorageConnectionString"));
// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve a reference to a container.
//specified container name
CloudBlobContainer container = blobClient.GetContainerReference("myBlobcontainer");
// Create the container if it doesn't already exist.
container.CreateIfNotExists();
container.SetPermissions(
new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
CloudBlockBlob blockBlob = container.GetBlockBlobReference("myBlob");
// Create or overwrite the "myblob" blob with contents from a local file.
using (var fileStream = file.InputStream)
{
blockBlob.UploadFromStream(fileStream);
}

Related

How to I display an image from my Azure Blob Storage Account in my ASP.NET Core Application?

I've had a look around and whilst I can see many guides on how to upload to Azure Blob Storage, they all seem to stop short of retrieving your data once you have done it. In my scenario I have an application where the user can upload an image to a car when they create it, I have created a service for my blob storage which I utilize with dependency injection.
When the user attempts to edit the car, I expect them to see the details and the related image. What I'm struggling with is returning that image, I'm almost there I think but not sure what to return my information as.
Here is my code starting with the submission form.
Create
#model Car
<div class="row">
<div class="col">
<form asp-action="Create" asp-controller="Car" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col">
<div class="md-form form-group">
<label asp-for="Name"></label>
<input type="text" class="form-control" asp-for="Name" />
</div>
<div class="md-form form-group">
<label asp-for="ImageFile" class="active">Image</label>
<!-- Image Upload-->
<kendo-upload name="ImageFile" show-file-list="true">
</kendo-upload>
</div>
</div>
</div>
<div class="row">
<div class="col">
<hr />
<button type="submit" class="btn btn-success">Submit</button>
<button type="reset" class="btn btn-amber">Reset</button>
</div>
</div>
</form>
</div>
</div>
Here are the corresponding methods for that Create view. As you'll see I open with the dependency injection and then utilize it within the method. This is only for the uploading of the blob and it's all working well, this is to provide the full context of my problem.
CarController.cs
private readonly ICarService _carService;
private readonly IWebHostEnvironment _env;
private readonly IConfiguration _configuration;
public CarController(
IWebHostEnvironment env,
ICarService carService,
IConfiguration configuration)
{
_carService = carService;
_env = env;
_configuration = configuration;
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Car car)
{
if (ModelState.IsValid)
{
//
//Create Car First
_carService.InsertCar(car);
//
//Get the id if the car just created
int id = car.Id;
//
//If file data is there, prepare and upload to
//blob storage.
if (car.ImageFile != null)
{
string category = "car";
var fileName = "car-image.jpg";
byte[] fileData = new byte[car.ImageFile.Length];
string mimeType = car.ImageFile.ContentType;
BlobStorageService objBlobService = new BlobStorageService(_configuration.GetConnectionString("AzureStorage"));
car.ImagePath = objBlobService.UploadFileToBlob(
category,
id,
fileName,
fileData,
mimeType);
}
return RedirectToAction(nameof(Index));
}
return View(car);
}
The next part is where I'm struggling. I need to show the uploaded image on the edit page. Here is the method I started writing to do this, it's incomplete and I need help from this point onwards.
Edit
[HttpGet]
public IActionResult Edit(int id)
{
//
//Access the service and pass the car ID to it
BlobStorageService objBlob = new BlobStorageService(_configuration.GetConnectionString("AzureStorage"));
objBlob.GetBlobData(id.ToString());
//
//Get car data from the repository
var data = _carService.GetCar(id);
return View(data);
}
Here is my blob storage service which handles most of this. As you can see when the ID is passed from the edit page it is used to form the directory structure on Azure to find the relative image. I'm returning the attributes but that's not right.
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
namespace MyProject.Services
{
public class BlobStorageService
{
string accessKey = string.Empty;
public BlobStorageService(string endPoint)
{
accessKey = endPoint;
}
public string UploadFileToBlob(string category, int id, string strFileName, byte[] fileData, string fileMimeType)
{
try
{
var _task = Task.Run(() => this.UploadFileToBlobAsync(category, id, strFileName, fileData, fileMimeType));
_task.Wait();
string fileUrl = _task.Result;
return fileUrl;
}
catch (Exception)
{
throw;
}
}
public async void GetBlobData(string id)
{
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(accessKey);
CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
string strContainerName = "uploads";
CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(strContainerName);
string pathPrefix = "car/" + id;
CloudBlobDirectory blobDirectory = cloudBlobContainer.GetDirectoryReference(pathPrefix);
// get block blob reference
CloudBlockBlob blockBlob = blobDirectory.GetBlockBlobReference("car-image.jpg");
await blockBlob.FetchAttributesAsync();
}
private async Task<string> UploadFileToBlobAsync(string category, int id, string strFileName, byte[] fileData, string fileMimeType)
{
try
{
string strContainerName = "uploads";
string fileName = category + "/" + id + "/" + strFileName;
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(accessKey);
CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(strContainerName);
if (await cloudBlobContainer.CreateIfNotExistsAsync().ConfigureAwait(false))
{
await cloudBlobContainer.SetPermissionsAsync(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob }).ConfigureAwait(false);
}
if (fileName != null && fileData != null)
{
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(fileName);
cloudBlockBlob.Properties.ContentType = fileMimeType;
await cloudBlockBlob.UploadFromByteArrayAsync(fileData, 0, fileData.Length).ConfigureAwait(false);
return cloudBlockBlob.Uri.AbsoluteUri;
}
return "";
}
catch (Exception ex)
{
throw (ex);
}
}
}
}
So, the question is, how do you return the URL for the image so that I can place it in an img src tag or download it for display? I've got myself a bit stuck and not sure. Whilst the directory structure works fine and it finds the file...I don't know how to utilise it.
Typically you do not return a URL at all, you return a huge block of bytes, a content-type saying "image/jpeg" (or similar) and that's what gets put in the browser. Your img src property just needs to refer to the controller action that returns those bytes.
eg a controller method that returns a file, without using the static files serving that's built into kestrel:
[ResponseCache(Duration = 1200, Location = ResponseCacheLocation.Client)]
public IActionResult GetImage(int id)
{
// determine file path from id and then
return PhysicalFile(path, "image/jpeg");
}
Now you can't return a PhysicalFile, but it only reads the bytes from the disk and returns them - its the same for any file, just change the content type. Try a FileResult, copying the returned data:
return File(bytes, "image/jpeg", fileName);
From MS
Shortly: CloudBlockBlob has properties Uri & StorageUri. First one directly points to file, second one allows to select primary or secondadry url.
There shouldn't be any problems if allow anonymous read of your blobs (configured on Azure Portal)

Filestream a blob from Azure storage

I have images in Azure that I need to add in a pdf using pdfJet.
This is the code that I use if I read an image on disk, however I have a lot of images and it does not make sense to download them from Azure.
Image image = new Image(objects, new BufferedStream(new FileStream(LocalPath + "image.PNG", FileMode.Open, FileAccess.Read)), ImageType.PNG);
PS: This is done in asp.net webforms.
Thank you for your help.
I am now using the following function to read a PDF:
public MemoryStream DownloadToMemoryStream(DTO.BlobUpload b)
{
CloudStorageAccount storageAccount = Conn.SNString(b.OrgID);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(b.Container);
CloudBlockBlob blob = container.GetBlockBlobReference(b.FileName);
var sasToken = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Read,
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(10),//assuming the blob can be downloaded in 10 miinutes
}, new SharedAccessBlobHeaders()
{
ContentDisposition = "attachment; filename=file-name"
});
using (MemoryStream ms = new MemoryStream())
{
blob.DownloadToStream(ms);
return ms;
}
}
And in the aspx page I use:
MemoryStream pdfScript = B.DownloadToMemoryStream(b);
to read the stream:
SortedDictionary<Int32, PDFobj> objects = pdfFinalScript.Read(pdfScript);
However I get the error message:
Cannot access a closed Stream
I have looked at how to open the stream but haven't managed to do it.
Could you please help, thank you
According to your description, you would download blobs from Azure.
Here are several ways you could refer to.
1.Download with blob url.
Create a Shared Access Signature with Read permission and Content-Disposition header set and create blob URL based on that and use that URL. In this case, the blob contents will be directly streamed from storage to the client browser.
2.Get the blob and DownloadFileFromBlob.
3.Download file to exactly path in local.
Webform:
You could use Response.Redirect(blobUrl); to redirect blob url and download it.
In .aspx:
<asp:Button ID="Button1" runat="server" Text="Click Me" OnClick="Button1_Click" />
In aspx.cs:
protected void Button1_Click(object sender, EventArgs e)
{
CloudStorageAccount account = new CloudStorageAccount(new StorageCredentials("accountname", "accountkey"), true);
var blobClient = account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("container");
var blob = container.GetBlockBlobReference("text.PNG");
var sasToken = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Read,
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(10),//assuming the blob can be downloaded in 10 miinutes
}, new SharedAccessBlobHeaders()
{
ContentDisposition = "attachment; filename=file-name"
});
using (MemoryStream ms = new MemoryStream())
{
blob.DownloadToStream(ms);
Image image = new Image(objects, ms, ImageType.PNG);
}
}

upload media to azure blob storage using asp.net web api 2

I'm developing rest web api that uploads images to my azure blob, I used one of the online tutorial and got this code
public class DocumentsController : ApiController
{
private const string CONTAINER = "documents";
// POST api/<controller>
public async Task<HttpResponseMessage> Post()
{
var context = new StorageContext();
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
// Get and create the container
var blobContainer = context.BlobClient.GetContainerReference(CONTAINER);
blobContainer.CreateIfNotExists();
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data and return an async task.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names for uploaded files.
foreach (var fileData in provider.FileData)
{
var filename = fileData.LocalFileName;
var blob = blobContainer.GetBlockBlobReference(filename);
using (var filestream = File.OpenRead(fileData.LocalFileName))
{
blob.UploadFromStream(filestream);
}
File.Delete(fileData.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
it is uploading the image in my account blob container but when i open azure storage manager and access the container i get wrong format as below picture describes
enter image description here
can u see the content-type ? I'm not able open this path in explorer
any help would be appreciated
Is your "filename" from code below have extension included when you debuging (like .jpg, .png)? For example "image.jpg"
var blob = blobContainer.GetBlockBlobReference(filename);
Also "fileData.LocalFileName" from code below need to have file extension
using (var filestream = File.OpenRead(fileData.LocalFileName))
It doesn't have extension and for this reason you have such issue

Uploading Base64 Image to Azure Blob

I have the following ASPX markup:
<asp:Image ImageUrl="placeholder.png" runat="server" ID="plhdr" />
The webpage lets a user upload an image, which is processed through a JavaScript library and then set as the source of the above control. The JavaScript sets the source as a Base64 String like this:
data:image/jpeg;base64,/9j/4AAQ...
I have a function on the same page that is meant to upload the displayed image to Azure Storage and then add a reference to it in Azure SQL. The code I have in the code behind is:
StorageCredentials creden = new StorageCredentials(accountname, accesskey);
CloudStorageAccount acc = new CloudStorageAccount(creden, useHttps: true);
CloudBlobClient client = acc.CreateCloudBlobClient();
CloudBlobContainer cont = client.GetContainerReference("testcont");
cont.CreateIfNotExists();
cont.SetPermissions(new BlobContainerPermissions
{
PublicAccess = BlobContainerPublicAccessType.Blob
});
CloudBlockBlob cblob = cont.GetBlockBlobReference("cblob");
var imagesrc = plhdr.ImageUrl;
cblob.UploadFromFile(#imagesrc);
var imageUrl = cblob.Uri;
Server Error in '/' Application. Could not find a part of the path
'D:\Windows\system32\placeholder.png'. Exception Details:
System.IO.DirectoryNotFoundException: Could not find a part of the
path 'D:\Windows\system32\dist\img\fling\space.gif'.
At this line: cblob.UploadFromFile(#imagesrc);.
Hoping someone can point me in the right direction.
According to your description, we can upload the image file to azure storage by using UploadFromStream(stream), Please have a try to do it with following sample code. It works for me.
CloudBlockBlob cblob = cont.GetBlockBlobReference("testblob");
var bytes = Convert.FromBase64String(#"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");// without data:image/jpeg;base64 prefix, just base64 string
using (var stream = new MemoryStream(bytes))
{
cblob.UploadFromStream(stream);
}

Displaying images from azure blob container

I have an asp mvc application where I am trying to display images that have been stored in Azure blob storage.
When I return the string the browser just shows as a broken image. When I right click and "inspect" and click the generated URI I recieve the following message
BlobNotFound
The specified blob does not exist. RequestId:f7c32876-0001-013e-539d-dede28000000 Time:2016-07-15T13:30:49.7560775Z
I have downloaded Azure Storage Explorer and can see the file. I have tried to change the access levels on the container to read access for blobs only from no public access (but I thought I did that in code with the line PublicAccess = BlobContainerPublicAccessType.Blob).
How do I display images that are contained in storage?
public string Index()
{
CloudBlockBlob blob = GetBlobInContainer("blobContainerTjsContainer", "PS-ICON-120x120_140.jpg");
return "<img src="+ blob.Uri.AbsoluteUri +" alt='PS Image'>";
}
CloudBlockBlob GetBlobInContainer(string container, string fileName)
{
//use web.config appSetting to get connection setting .NET Framework's ConfigurationManager class can also be used for this
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
//create the blob client
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
//retrieve a refernce to a container
CloudBlobContainer blobContainer = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting(container));
//set permission to show to public
blobContainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob});
blobContainer.CreateIfNotExists();
// Retrieve reference to a blob named "photo1.jpg".
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(fileName);
return blockBlob;
}
EDIT
After further reading I have changed
// Retrieve reference to a blob named "photo1.jpg".
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(fileName);
return blockBlob;
to
// Retrieve reference to a blob named "photo1.jpg".
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(fileName);
blockBlob.Properties.ContentType = "image/jpg";
blockBlob.SetProperties();
return blockBlob;
this changes returns an exception
Exception Details: System.Net.WebException: The remote server returned an error: (404) Not Found.
Do I need to do something different at the time of uploading?
Edit
#Gaurav Mantri
I can see that it is stored as application/octet-stream but when I tried to change this to image/jpg using
blockBlob.Properties.ContentType = "image/jpg";
blockBlob.SetProperties();
I got a 404 exception at set properties. When I pull the image, while debugging I can see the image name is the same as it was when I uploaded it
at the time of uploading I used the below code
void UploadFile(string filePath)
{
//use web.config appSetting to get connection setting .NET Framework's ConfigurationManager class can also be used for this
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
//create the blob client
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
//retrieve a refernce to a container
CloudBlobContainer blobContainer = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting("blobContainerTjsContainer"));
//create a container if it doesnt exist
blobContainer.CreateIfNotExists();
//gets the reference to the blob that will be written or OVER written
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference("my-image-blob");
using (var fileStream = System.IO.File.OpenRead(filePath))
{
blockBlob.UploadFromStream(fileStream);
}
}
Thanks to the comments from Gaurav I was able to work out my problem.
I was downloading the file as below
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(fileName);
blockBlob.Properties.ContentType = "image/jpg";
blockBlob.SetProperties();
return blockBlob;
But when storing the file I was not setting the content type so it was being saved as application/octet-stream and the above code was trying to convert the type when calling the file. By setting the type prior to uploading I was able to simply call the file and have it displayed as an image e.g.
CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference("my-image-blob");
blockBlob.Properties.ContentType = "image/jpg";
using (var fileStream = System.IO.File.OpenRead(filePath))
{
blockBlob.UploadFromStream(fileStream);
}

Categories