pr0g33k

Managing Files Using Microsoft Azure Blob Storage (Part 2)

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. Now let's add a view to list the files we've uploaded.

To start, add an ActionResult function to the FileManagerController named "List" and create a view for it (List.cshtml).

Now, add some ActionLink's to the Index.cshtml view to navigate to our List view.

@{
    ViewBag.Title = "File Manager";
}
<p>@Html.ActionLink("View Images", "List", new { id = "Images" })</p>
<p>@Html.ActionLink("View Files", "List", new { id = "Files" })</p>
    

The ActionLinks navigate to our List view - one for images and one for files. The URL's are: "/FileManager/List/Images" and "/FileManager/List/Files"

To pass the name of the container and the list of URL's from Azure Blob Storage, I created the following class and placed it in my Models folder:

public class ResourcesModel
{
    public String Container { get; set; }
    public List<String> Urls { get; set; }
}
    

Change the List controller action to look like this:

public ActionResult List(String id)
{
    ResourcesModel resourcesModel = new ResourcesModel();

    if (!String.IsNullOrEmpty(id))
    {
        CloudBlobContainer cloudBlobContainer = GetCloudBlobContainer(id);
        BlobResultSegment blobResultSegment = cloudBlobContainer.ListBlobsSegmented(null);

        resourcesModel.Container = id;
        resourcesModel.Urls = blobResultSegment.Results.Select(r => r.Uri.ToString()).ToList();

        while (blobResultSegment.ContinuationToken != null)
        {
            blobResultSegment = cloudBlobContainer.ListBlobsSegmented(blobResultSegment.ContinuationToken);
            resourcesModel.Urls.AddRange(blobResultSegment.Results.Select(r => r.Uri.ToString()));
        }
    }

    return View(resourcesModel);
}

The GetCloudBlobContainer function is listed in part 1.

I use CloudBlobContainer.ListBlobsSegmented() here because the number of blobs you can return from a single call is limited to 5000. If there are more than 5000 blobs, a continuation token is sent back with the request. The continuation token is basically a marker that can be used by subsequent calls to pick up where the last call left off. First, I call ListBlobsSegmented and pass null for the continuation token to get as many items up to the limit. Then, I loop for as long as a continuation token is returned and get those results, too. I'm only interested in the URI of each blob so I use the Linq Select() extension method to query out the URI's and add them to my Urls (List<String>) collection.

Here's the List.cshtml view:

@model RobertGaut.Pr0g33k.Web.Areas.Admin.Models.ResourcesModel
@using RobertGaut.Core.Extensions
@{
    ViewBag.Title = "Resources";
}
<p>@Html.ActionLink(String.Format("Upload to {0}", Model.Container.ToTitleCase()), "Upload", new { id = Model.Container.ToTitleCase() })</p>
<ul id="resources">
    @foreach (String url in Model.Urls)
    {
        <li>@Html.ActionLink(Path.GetFileName(url), "Edit", new { id = HttpServerUtility.UrlTokenEncode(System.Text.UTF8Encoding.ASCII.GetBytes(url)), container = Model.Container }, new { data_href = url })</li>
    }
</ul>
<div id="preview"></div>
@section scripts{
    <script>
        $(document).ready(function () {
            $("#resources > li").each(function () {
                var lip = $(this).position();
                var liw = $(this).width();
                var a = $(this).children('a').first();
                var href = a.attr("data-href");
                var ext = href.substr(href.lastIndexOf('.') + 1);
                $(this).css({ "background": "url(/images/" + ext + ".png) no-repeat 50% 0" });
                switch (ext) {
                    case "jpg":
                    case "png":
                    case "gif":
                        a.hover(function () {
                            $("#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({ "position": "absolute", "top": lip.top - h2 + 105 + "px", "left": Math.floor((lip.left + (liw / 2)) - (w2 / 2)) + "px" });
                                $("#preview > div").first().text(w + " X " + h);
                            });
                        }, function () {
                            $("#preview").html('');
                            $("#preview").hide();
                        });
                        break;
                }
            });
        });
    </script>
}
    

There's a lot going on there, jQuery-wise, but basically I check the extension of each file and set a background image to the <li> that represents that file type. Then, if the file is an image, I create a hover effect to display a smaller preview. This keeps the browser from downloading every image when this page loads. Images are only downloaded when the user hovers over its anchor tag.

I have a small amount of CSS that accompanies this view:

ul#resources {
    margin: 0;
    padding: 0;
}

    ul#resources li {
        display: inline-block;
        height: 142px;
        margin: 5px;
        position: relative;
        min-width: 310px;
    }

        ul#resources li a {
            border: 0;
            top: 128px;
            display: block;
            position: relative;
            text-align: center;
            width: 100%;
        }

div#preview {
    background: #000;
    border: 2px solid #000;
    display: none;
    text-align: center;
}

    div#preview > div {
        color: #fff;
        height: 20px;
    }
    

In part 3 we'll add a view to edit the file.

Comments:

Leave a comment
  1. CAPTCHA