DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modern Digital Website Security: Prepare to face any form of malicious web activity and enable your sites to optimally serve your customers.

Containers Trend Report: Explore the current state of containers, containerization strategies, and modernizing architecture.

Low-Code Development: Learn the concepts of low code, features + use cases for professional devs, and the low-code implementation process.

E-Commerce Development Essentials: Considering starting or working on an e-commerce business? Learn how to create a backend that scales.

Related

  • All Things ASP.NET Core and MVC: Tutorials and Articles
  • Add Watermark Text to Images in ASP.NET MVC
  • Identify ASP.NET MVC Assembly Version
  • How to Make Custom Error Pages Work in ASP.NET MVC 5

Trending

  • Demystifying Databases, Data Warehouses, Data Lakes, and Data Lake Houses
  • Micro Frontends Architecture
  • Exploring Apache Airflow for Batch Processing Scenario
  • An In-Depth Exploration of REST, gRPC, and GraphQL in Web Projects
  1. DZone
  2. Coding
  3. Frameworks
  4. Dynamically Resize Your ASP.NET MVC Images

Dynamically Resize Your ASP.NET MVC Images

Delivering a proper image size can be crucial to insuring that your web application loads as quickly as possible. In this post, learn how to dynamically resize your images in your ASP.NET MVC application!

Jonathan Danylko user avatar by
Jonathan Danylko
·
Nov. 22, 16 · Opinion
Like (3)
Save
Tweet
Share
13.9K Views

Join the DZone community and get the full member experience.

Join For Free
Tiny Green Plastic Frog

With the new redesign behind me, I decided to check out some performance metrics to see how fast the site performed.

I decided to test it out on GTMetrix.com.

GTMetrix Scorecard Before Optimization

Hmm...Ouch.

After looking over the results, I noticed the six images from the blog posts were dragging it down giving me a 'B' (better than an 'F', I guess).

My problem was delivering the full 640x480 blog post images to the browser, defining a constrained area where I shove an image into a DIV, and let the container/Bootstrap take care of everything for me since it handles responsive so well.

The problem with this thinking is the amount of bytes transferred over the wire to the browser. You need to shrink the images to the correct dimensions before it's sent to the browser.

This will give you an increase in performance by sending less bytes to the browser making your users even happier.

You could achieve this by one of two ways:

  • Use a task runner to resize all of the images and store them in a thumbnail folder before deployment
  • Dynamically shrink the images when requested

Since I didn't want to tie up more storage on a hosted server, I decided to implement the latter.

An Image Request Is Still a Request

We can use controllers to retrieve a trimmed down version of an image.

In the image tag, we define the source as a Url.Action pointing to a controller action that returns a resized version of our image.

All we need is the name of the file along with an ImageResult ActionResult.

First, we start with our controller action.

public ActionResult Thumbnail(string filename)
{
    var img = new WebImage(filename)
        .Resize(291, 285, false, true);
    return new ImageResult(new MemoryStream(img.GetBytes()), "binary/octet-stream");
}

Since I only want to resize the home page images for now, I placed mine on my Home controller. You could even create a Services controller and place it there.

We also need our image dimensions for our image tag so I'll hard code these and refactor later.

I also want to bring to your attention a WebHelper class called WebImage.

This WebHelper provides the basics for manipulating images dynamically. While I'm only resizing the image, the WebImage class can:

  • Add an image or text watermark
  • Clone
  • Crop
  • Flip Horizontally/Vertically
  • Rotate Left/Right

It uses a fluent syntax so you can Resize, Flip Horizontally, and add a text watermark before you send the image back.

ImageResult ActionResult

The ImageResult is a simple ActionResult and returns back a binary/octet-stream.

public class ImageResult : ActionResult
{
    public ImageResult(Stream imageStream, string contentType)
    {
        if (imageStream == null)
            throw new ArgumentNullException("imageStream");
        if (contentType == null)
            throw new ArgumentNullException("contentType");

        this.ImageStream = imageStream;
        this.ContentType = contentType;
    }

    public Stream ImageStream { get; set; }
    public string ContentType { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var response = context.HttpContext.Response;

        response.ContentType = this.ContentType;

        byte[] buffer = new byte[4096];
        while (true)
        {
            int read = this.ImageStream.Read(buffer, 0, buffer.Length);
            if (read == 0)
                break;

            response.OutputStream.Write(buffer, 0, read);
        }

        response.End();
    }
}

To optimize our performance, it's best to use streams when sending bytes through the pipe.

I've seen people load the image into memory and then send it out. Writing out to the OutputStream provides a faster way to deliver images.

What Does the Razor Syntax Look Like?

The Razor syntax may look a little strange, but it's a quick and dirty way of dynamically returning images.

<img src="@Url.Action("Thumbnail", "Home", new {FileName="~/Content/Images/Cop.jpg"})"
     class="img-responsive"
     title="@post.Title"
     alt="@post.Title" width="291" height="285" />

Simple, right?

I also found out that I get 'dinged' on points when I don't specify an image's dimensions for the browser. This way, it defines the area and then fills in the image from the stream.

If I don't have the dimensions, the browser has to wait for the image to completely download delaying the rendering of the layout.

Can We Make It Even Faster?

After thinking about this a little more, we can add even more performance to the site.

Since a controller can use ActionFilters, we can apply caching to the Action as well.

[ETag, OutputCache(Duration = 3600, VaryByParam = "filename")]
public ActionResult Thumbnail(string filename)
{
    var img = new WebImage(filename)
        .Resize(291, 285, false, true);
    return new ImageResult(new MemoryStream(img.GetBytes()), "binary/octet-stream");
}

The OutputCache is set for 1 hour and will cache the result by filename.

The ETag is an ActionFilter used to determine whether an asset requires updated or not. I've talked about this performant ActionFilter as one of my favorite ASP.NET MVC ActionFilters.

Did It Work?

After I put everything in place, I went back to GTMetrix.com and ran another test.

GTMetrix Scorecard After Optimization

Not bad.

I saw all green on my results, my page load time decreased as well as my Total Page Size, but my requests went up a little. Yikes!

Also, I'm .9 milliseconds off from losing my audience according to web statistics (40% abandon a website that takes longer than 3 seconds to load).

Of course, I will be spending a little more time to get that .9 milliseconds down.

Conclusion

Today, I presented a way to scale your images down to a moderate size using an ImageResult and a WebImage class. You should always be optimizing your site for the best possible performance you can squeeze out of your code. Images are just one aspect of optimizing your site.

I'll talk more about optimizing your ASP.NET MVC site in the future. If you have certain performance questions, please post them in the comments below.

Post your comments about how to shift your MVC site into high gear.

ASP.NET MVC ASP.NET

Published at DZone with permission of Jonathan Danylko, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • All Things ASP.NET Core and MVC: Tutorials and Articles
  • Add Watermark Text to Images in ASP.NET MVC
  • Identify ASP.NET MVC Assembly Version
  • How to Make Custom Error Pages Work in ASP.NET MVC 5

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: