0

I'm currently able to upload and display an image from my blob storage but i'm struggling to find a way to 'update/replace' that image if a user would like to change it. I'm happy with either of two methods:

  1. Replace the image and keep the same url
  2. Upload a new image and reference a new url in the database

In the controller i'm using Dependency Injection for the photoService:

MANAGE CONTROLLER

//
    // GET: /Manage/Index
    public async Task<ActionResult> Index(ManageMessageId? message)
    {
        ViewBag.StatusMessage =
            message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
            : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
            : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
            : message == ManageMessageId.Error ? "An error has occurred."
            : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
            : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
            : "";

        var userId = User.Identity.GetUserId();
        var model = new IndexViewModel
        {
            HasPassword = HasPassword(),
            PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
            TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
            Logins = await UserManager.GetLoginsAsync(userId),
            BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
        };


        // PhotoService
        var user = new ApplicationUser
        {
            PhotoUrl = await _photoService.UploadPhotoAsync(model.Photo)
        };
        await UserManager.CreateAsync(user);
        // PhotoService END



        return View(model);
    }

SERVICE

public class PhotoService : IPhotoService
{
    public async void CreateAndConfigureAsync()
    {
        try
        {
            CloudStorageAccount storageAccount = StorageUtils.StorageAccount;

            // Create a blob client and retrieve reference to images container
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference("images");

            // Create the "images" container if it doesn't already exist.
            if (await container.CreateIfNotExistsAsync())
            {
                // Enable public access on the newly created "images" container
                await container.SetPermissionsAsync(
                    new BlobContainerPermissions
                    {
                        PublicAccess =
                            BlobContainerPublicAccessType.Blob
                    });

                // Logging
            }
        }
        catch (Exception ex)
        {
            // Logging
        }
    }

    public async Task<string> UploadPhotoAsync(HttpPostedFileBase photoToUpload)
    {
        if (photoToUpload == null || photoToUpload.ContentLength == 0)
        {
            return null;
        }

        string fullPath = null;
        Stopwatch timespan = Stopwatch.StartNew();

        try
        {
            CloudStorageAccount storageAccount = StorageUtils.StorageAccount;

            // Create the blob client and reference the container
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference("images");

            // Create a unique name for the images we are about to upload
            string imageName = String.Format("task-photo-{0}{1}",
                Guid.NewGuid().ToString(),
                Path.GetExtension(photoToUpload.FileName));

            // Upload image to Blob Storage
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(imageName);
            blockBlob.Properties.ContentType = photoToUpload.ContentType;
            await blockBlob.UploadFromStreamAsync(photoToUpload.InputStream);

            // Convert to be HTTP based URI (default storage path is HTTPS)
            var uriBuilder = new UriBuilder(blockBlob.Uri);
            uriBuilder.Scheme = "http";
            fullPath = uriBuilder.ToString();

            timespan.Stop();
            //log.TraceApi("Blob Service", "PhotoService.UploadPhoto", timespan.Elapsed, "imagepath={0}", fullPath);
        }
        catch (Exception ex)
        {
            //log.Error(ex, "Error upload photo blob to storage");
        }

        return fullPath;
    }
}

INTERFACE

public interface IPhotoService
{
    void CreateAndConfigureAsync();
    Task<string> UploadPhotoAsync(HttpPostedFileBase photoToUpload);
}

MODEL

public class IndexViewModel
{
    public bool HasPassword { get; set; }
    public IList<UserLoginInfo> Logins { get; set; }
    public string PhoneNumber { get; set; }
    public bool TwoFactor { get; set; }
    public bool BrowserRemembered { get; set; }
    public HttpPostedFileBase Photo { get; set; }
    public string PhotoUrl { get; set; }
}

VIEW

@using Microsoft.AspNet.Identity
@model AzureBlobStorageTest.Models.IndexViewModel
@{
ViewBag.Title = "Manage";
}

<h2>@ViewBag.Title.</h2>

 <p class="text-success">@ViewBag.StatusMessage</p>
 <div>
<h4>Change your account settings</h4>
<h5>Image:</h5>
<img src="@(Model.PhotoUrl)" alt="Photo"/>


@using (Html.BeginForm("Index", "Manage", FormMethod.Post, new { role = "form", enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary("", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(m => m.Photo, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Photo, new { type = "file" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-default" value="Register" />
        </div>
    </div>
}
</div>

It would be great if someone could help me with this as I've been trying all sorts of ways but having no luck.

Please let me know if you require any further info.

Thanks

1
  • Using await blockBlob.UploadFromStreamAsync(photoToUpload.InputStream); will overwrite the existing file if it exists so it should work without doing anything Commented Dec 11, 2017 at 23:42

1 Answer 1

1

As Thomas commented that CloudBlockBlob.UploadFromStreamAsync would upload a stream to a block blob and if the blob already exists, it will be overwritten. But your blob name is unique under the UploadPhotoAsync method as follows:

string imageName = String.Format("task-photo-{0}{1}",
            Guid.NewGuid().ToString(),
            Path.GetExtension(photoToUpload.FileName));

I would recommend you define a new method (e.g. DeletePhotoAsync) under IPhotoService, and retrieve the existing image then delete it before invoking the UploadPhotoAsync method for uploading the new image.

Or you could add a optional parameter names photoUrl for the UploadPhotoAsync method, and if the photoUrl is not null or empty, then you could initialize your imageName as follows:

imageName = new CloudBlockBlob(new Uri($"{photoUrl}")).Name;

UPDATE:

Your PhotoService would look like this:

public class PhotoService:IPhotoService
{
    CloudBlobContainer container;
    public PhotoService()
    {
        CloudStorageAccount storageAccount = StorageUtils.StorageAccount;
        // Create a blob client and retrieve reference to images container
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
        container= blobClient.GetContainerReference("images");
    }

    //photoUrl: https://<account-name>.blob.core.windows.net/images/task-photo-09e0e292-8df2-4630-81a5-cb4977eef1f9.png
    public async Task<bool> DeletePhotoAsync(string photoUrl)
    {
        string blobName = new CloudBlockBlob(new Uri(photoUrl)).Name;
        var targetBlob = container.GetBlockBlobReference(blobName);
        return await targetBlob.DeleteIfExistsAsync();
    }

    public async void CreateAndConfigureAsync()
    {
      try
      {
        // Create the "images" container if it doesn't already exist.
        if (await container.CreateIfNotExistsAsync())
        {
            // Enable public access on the newly created "images" container
            await container.SetPermissionsAsync(
                new BlobContainerPermissions
                {
                    PublicAccess =
                        BlobContainerPublicAccessType.Blob
                });

            // Logging
        }
     }
     catch (Exception ex)
     {
        // Logging
     }
  }

  //UploadPhotoAsync
}
Sign up to request clarification or add additional context in comments.

6 Comments

Hi Bruce, thanks again for helping me out. How would DeletePhotoAsync look in my interface? Can you provide an example for me?
I just updated my answer with the code sample, you could refer to it.
Thanks Bruce, that is exactly what I needed. Can you just confirm if i'm calling the PhotUrl correctly in the controller? I'm not currently getting any image coming through from the Manage view
For your /Manage/Index action, you did not set the value to the property Photo under IndexViewModel model, so the await _photoService.UploadPhotoAsync(model.Photo) would return null. For await UserManager.CreateAsync(user);, it would create a user with no password. Per my understanding, these code has no relationship with your question.
/Manage/Index is used to read the user info, you need to retrieve the user photo. And you could call var user=await UserManager.FindByIdAsync(userId); model.Photo=user.PhotoUrl; for retrieving the user info and display the image in the Index.cshtml. For updating the photo, you could follow the page for registering the account and provide the feature for uploading the new image. then under the ManageController you need to define an new action for uploading the new image, and add the similar code snippet:
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.