pr0g33k

Managing Files Using Microsoft Azure Blob Storage (Part 3)

In part 1, we set up an MVC project to connect to Microsoft's Azure Blob Storage and also created a view to upload some files. In part 2, we created a view to list the files we uploaded. Now let's add a view to display an individual file and delete it.

First, we need to create an object to pass aroung our data. I created the following class to handle that:

public class FileEditModel
{
    public String Container { get; set; }
    public String Url { get; set; }
    public String FileName { get; set; }
}
    

Next, add an action to the FileManagerController named Edit and create a strongly-typed (FileEditModel) view for it:

public ActionResult Edit(String id, String container)
{
    String url = UTF8Encoding.ASCII.GetString(HttpServerUtility.UrlTokenDecode(id));
    FileEditModel fileEditModel = new FileEditModel()
    {
        Container = container,
        Url = url,
        FileName = Path.GetFileName(url)
    };

    return View(fileEditModel);
}
    

In the List.cshtml view (part 2), there's an ActionLink that links to the Edit ActionResult/view. The "id" RouteParameter is a URL to the file which is encoded using HttpServerUtility.UrlTokenEncode(). I discuss this in more detail here. In our Edit ActionResult, we decode that "id" RouteParameter using HttpServerUtility.UrlTokenDecode(). The container is passed to the Edit ActionResult as a QueryString parameter. I debated whether I should attempt to parse the container from the URL but decided to explicitly pass it in the QueryString. I realize that it's possible for someone to tamper with the QueryString but this is, after all, intended for the "admin" section of the site and presumably limited to someone in an administrator role who knows what they're doing. If you're not able to trust the person accessing this functionality, you might consider parsing the container from the URL or finding another way to pass the container so that it's more foolproof.

The Edit.cshtml view looks like this:

@model RobertGaut.Pr0g33k.Web.Areas.Admin.Models.FileEditModel
@{
    ViewBag.Title = "Edit";
}
<p>@Model.Url</p>
<p><a href="@Model.Url" target="_blank">Download File/Open in a new window</a></p>
<div id="preview"></div>
@using (Html.BeginForm())
{
    @Html.HiddenFor(m => m.Container)
    @Html.HiddenFor(m => m.FileName)
    @Html.HiddenFor(m => m.Url)
    <p><input name="actionType" type="submit" value="Delete" onclick="return confirm('Are you sure you want to delete this file?')" /></p>
}
@section scripts{
    <script>
        $(document).ready(function () {
            var href = "@Model.Url";
            var ext = href.substr(href.lastIndexOf('.') + 1);
            switch (ext) {
                case "jpg":
                case "png":
                case "gif":
                    $("#preview").html("<img id='preview-image' src='" + href + "' /><div></div>");
                    $("#preview-image").load(function () {
                        $("#preview").show();
                        var h = $(this).height();
                        var w = $(this).width();
                        var h2 = h > 240 ? 240 : h;
                        var w2 = Math.floor((h2 / h) * w);
                        $(this).css({ "height": h2 + "px", "width": w2 + "px" });
                        $("#preview").css({ "width": w2 + "px" });
                        $("#preview > div").first().text(w + " X " + h);
                    });
                    break;
            }
        });
    </script>
}
    

The jQuery script is similar to the script used on the List.cshtml view - it just displays an icon for the file type and creates a hover effect to display an image preview for image file types.

I didn't add any functionality to rename or otherwise edit the file, just the ability to delete it. I'm not really interested in renaming and, really, it requires deleting and uploading a new file anyway. There isn't a way in the Azure Blob Storage API to rename blobs, per se. There is a way to copy a blob with a new name but the result is the same - create a new blob with the desired name and delete the old one.

Posting takes you here:

[HttpPost]
public ActionResult Edit(String actionType, FileEditModel fileEditModel)
{
    CloudBlobContainer cloudBlobContainer = GetCloudBlobContainer(fileEditModel.Container);
    CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(fileEditModel.FileName);

    switch (actionType.ToLower())
    {
        case "delete":
            cloudBlockBlob.DeleteIfExists();
            break;
    }

    return RedirectToAction("List", new { id = fileEditModel.Container });
}
    

I used a switch here to allow future growth for other features (I know it looks silly to have a switch with just one option). GetCloudBlobContainer is defined in part 1. The code should be pretty self-explanatory: get a reference to the container, use the container to get a reference to the blob, delete the blob is if exists, go back to the list.

I've been using this for a few days now and the workflow for managing files suits my needs pretty well (for now). I generally like to keep things very simple and straightforward. If you have any suggestions on other features or how to make this better, though, I'd love to hear them.

Comments:

  1. Joel Ransom

    Seems like your "Part 2" link displays Part 1 even though the url is http://www.pr0g33k.com/Blog/5/Managing-Files-Using-Microsoft-Azure-Blog-Storage-Part-2
  2. pr0g33k

    Thanks for catching that. I fixed it.
Leave a comment
  1. CAPTCHA