Rendering smallest possible image size with MVC3 v

2019-06-07 15:00发布

问题:

I am in the process of moving a webforms app to MVC3. Ironically enough, everything is cool beans except one thing - images are served from a handler, specifically the Microsoft Generated Image Handler. It works really well - on average a 450kb photo gets output at roughly 20kb.

The actual photo on disk weighs in at 417kb, so i am getting a great reduction.

Moving over to MVC3 i would like to drop the handler and use a controller action. However i seem to be unable to achieve the same kind of file size reduction when rendering the image. I walked through the source and took an exact copy of their image transform code yet i am only achieving 230~kb, which is still a lot bigger than what the ms handler is outputting - 16kb.

You can see an example of both the controller and the handler here

I have walked through the handler source code and cannot see anything that is compressing the image further. If you examine both images you can see a difference - the handler rendered image is less clear, more grainy looking, but still what i would consider satisfactory for my needs.

Can anyone give me any pointers here? is output compression somehow being used? or am i overlooking something very obvious?

The code below is used in my home controller to render the image, and is an exact copy of the FitImage method in the Image Transform class that the handler uses ...

public ActionResult MvcImage()
    {
        var file = Server.MapPath("~/Content/test.jpg");
        var img = System.Drawing.Image.FromFile(file);
        var sizedImg = MsScale(img);

        var newFile = Server.MapPath("~/App_Data/test.jpg");
        if (System.IO.File.Exists(newFile))
        {
            System.IO.File.Delete(newFile);
        }
        sizedImg.Save(newFile);
        return File(newFile, "image/jpeg");
    }

private Image MsScale(Image img)
    {
        var scaled_height = 267;
        var scaled_width = 400;
        int resizeWidth = 400;
        int resizeHeight = 267;
        if (img.Height == 0)
        {
            resizeWidth = img.Width;
            resizeHeight = scaled_height;
        }
        else if (img.Width == 0)
        {
            resizeWidth = scaled_width;
            resizeHeight = img.Height;
        }
        else
        {
            if (((float)img.Width / (float)img.Width < img.Height / (float)img.Height))
            {
                resizeWidth = img.Width;
                resizeHeight = scaled_height;
            }
            else
            {
                resizeWidth = scaled_width;
                resizeHeight = img.Height;
            }
        }

        Bitmap newimage = new Bitmap(resizeWidth, resizeHeight);
        Graphics gra = Graphics.FromImage(newimage);
        SetupGraphics(gra);
        gra.DrawImage(img, 0, 0, resizeWidth, resizeHeight);
        return newimage;
    }

    private void SetupGraphics(Graphics graphics)
    {
        graphics.CompositingMode = CompositingMode.SourceCopy;
        graphics.CompositingQuality = CompositingQuality.HighSpeed;
        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphics.SmoothingMode = SmoothingMode.HighSpeed;
    }

回答1:

If you don't set the quality on the encoder, it uses 100 by default. You'll never get a good size reduction by using 100 due to the way image formats like JPEG work. I've got a VB.net code example of how to set the quality parameter that you should be able to adapt.

80L here is the quality setting. 80 still gives you a fairly high quality image, but at DRASTIC size reduction over 100.

            Dim graphic As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(newImage)
            graphic.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
            graphic.SmoothingMode = Drawing.Drawing2D.SmoothingMode.HighQuality
            graphic.PixelOffsetMode = Drawing.Drawing2D.PixelOffsetMode.HighQuality
            graphic.CompositingQuality = Drawing.Drawing2D.CompositingQuality.HighQuality
            graphic.DrawImage(sourceImage, 0, 0, width, height)

            ' now encode and send the new image
            ' This is the important part

            Dim info() As Drawing.Imaging.ImageCodecInfo = Drawing.Imaging.ImageCodecInfo.GetImageEncoders()
            Dim encoderParameters As New Drawing.Imaging.EncoderParameters(1)

            encoderParameters.Param(0) = New Drawing.Imaging.EncoderParameter(Drawing.Imaging.Encoder.Quality, 80L)
            ms = New System.IO.MemoryStream
            newImage.Save(ms, info(1), encoderParameters)

When you save or otherwise write the image after setting the encoder parameters, it'll output it using the JPEG encoder (in this case) set to quality 80. That will get you the size savings you're looking for.



回答2:

I believe it's defaulting to PNG format also, although Tridus' solution solves that also.

However, I highly suggest using this MVC-friendly library instead, as it avoids all the image resizing pitfalls and doesn't leak memory. It's very lightweight, free, and fully supported.