How to show the selected image file without saving file in the disk before upload

Demo:  

I was thinking a way to show images before actually saving them on the server. I would say to preview images using javascript. Obviously asp.net file input control does not like post-backs when it has a selected file. This idea is to preview the selected image by saving the file as byte[] array in the session and assigning the file to a src (ImageUrl) of a image element(asp.net Image) in the very subsequent request. I have used jQuery to simplify the battle with asp.net rendered domain model. First hurdle, simple jQuery ajax didnt posted my selected image file. So I needed to find a way to post my selected file, then found a nice little plugin so called jQuery AjaxForm. This plugin nicely did the trick. I manage to change the form action for a bit and post the form to GenericHanlder. As soon I found the posted file, I get the converted file in to a System.Drawing.Image and saved it in the session. Finally carefully reverted the asp.net from action to it's original value.

HttpPostedFile file = context.Request.Files[0];
image = Bitmap.FromStream(file.InputStream);
context.Session["image"] = image;
If we request GenericHanlder again, it can retreive the image from session and serve as an image. So what I did was in the success event of jQuery AjaxForm post, assign the GenericHandler.ashx to src of the preview image. As a result, there will be a second subsequent request just after the first form post to the Generic Hander. Now I distinguish the two requests and for the second request I serve the image from the session after converting image object in to a byte[] array using a momery stream.
MemoryStream stream = new MemoryStream();
image.Save(stream, format);
context.Response.BinaryWrite(stream.ToArray());

Download jQuery Ajax Form

Download jQuery

Memeory:

Saving the image in the session is only between two consecutive requests. First request creates the image and save it in the session. Then the second request consumes the image, remove the image from session and dispose it. So memory use of the server is instantaneous. Producer consumer fashion.

Markup:

Snippet

<%@ Page Language="C#" %>
<html>
<head id="Head2" runat="server">
    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery-form.js" type="text/javascript"></script>
    <script language="javascript" type="text/javascript">
        function ShowFile() {
            var formId = '<%= this.Form.ClientID %>';
            var imageId = '<%=this.imageView.ClientID %>';
            var fileUploadId = '<%=fuFileUpload.UniqueID %>';
            var r = Math.floor(Math.random() * 999999);
            var action = $('#' + formId).attr('action');
            $('#' + formId).attr('action''GenericHandler.ashx?f=' + fileUploadId);
            $('#' + formId).ajaxForm(function () {
                $('#' + imageId).attr('src''GenericHandler.ashx?s=' + fileUploadId + '&r=' + r);
                $('#' + imageId).show();
                $('#' + formId).attr('action', action);
            });
            $('#' + formId).submit();
        }
    </script>
</head>
<body>
    <form id="form2" runat="server">
    <asp:FileUpload runat="server" ID="fuFileUpload" onchange="javascript:ShowFile();" />
    <asp:Button ID="Button1" runat="server" Text="Save" />
    <hr />
    <asp:Image ID="imageView" runat="server" alt="Thumbnail" Style="displaynone" />
    </form>
</body>
</html>

Generic Handler:

Snippet

public class GenericHandler : IHttpHandlerIRequiresSessionState
{
    public void ProcessRequest(HttpContext context)
    {
        string f = context.Request.QueryString.Get("f");
        string s = context.Request.QueryString.Get("s");
        if (!string.IsNullOrEmpty(f))
        {
            HttpPostedFile file = context.Request.Files[f];
            if (file == null)
                HttpContext.Current.ApplicationInstance.CompleteRequest();
            else
            {
                List<string> keys = new List<string>();
                foreach (string key in context.Session.Keys) if (key.StartsWith(f)) keys.Add(key);
                foreach (string key in keys) context.Session.Remove(key);
                System.Drawing.Image image = Bitmap.FromStream(file.InputStream);
                context.Session[f + "image"] = image;
                context.Session[f + "contextType"] = context.Request.Files[0].ContentType;
                context.Response.Flush();
                HttpContext.Current.ApplicationInstance.CompleteRequest();
            }
        }
        else if (!string.IsNullOrEmpty(s))
        {
            string ck = s + "contextType";
            string ik = s + "image";
            if (context.Session[ck] == null || context.Session[ik] == null)
            {
                HttpContext.Current.ApplicationInstance.CompleteRequest();
                if (context.Session[ck] != null) context.Session.Remove(ck);
                if (context.Session[ik] != null) context.Session.Remove(ik);
            }
            else
            {
                using (System.Drawing.Image image = (System.Drawing.Image)context.Session[ik])
                {
                    context.Response.Clear();
                    context.Response.ClearHeaders();
                    string type = context.Session[ck].ToString().ToLower();
                    System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Gif;
                    bool isValid = true;
                    if (type.Contains("bmp")) format = System.Drawing.Imaging.ImageFormat.Bmp;
                    else if (type.Contains("jpg") || type.Contains("jpeg")) format = System.Drawing.Imaging.ImageFormat.Jpeg;
                    else if (type.Contains("png")) format = System.Drawing.Imaging.ImageFormat.Png;
                    else if (type.Contains("gif")) format = System.Drawing.Imaging.ImageFormat.Gif;
                    else if (type.Contains("wmf")) format = System.Drawing.Imaging.ImageFormat.Wmf;
                    else if (type.Contains("tiff") || type.Contains("tif")) format = System.Drawing.Imaging.ImageFormat.Tiff;
                    else if (type.Contains("exif")) format = System.Drawing.Imaging.ImageFormat.Exif;
                    else isValid = false;
                    if (isValid)
                    {
                        using (MemoryStream stream = new MemoryStream())
                        {
                            image.Save(stream, format);
                            context.Response.BinaryWrite(stream.ToArray());
                            context.Response.ContentType = type;
                        }
                    }
                    context.Session.Remove(ck);
                    context.Session.Remove(ik);
                    HttpContext.Current.ApplicationInstance.CompleteRequest();
                }
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}
References: Many thanks to following people for their work, without them I would not have achive this.
  1. jQuery Form:
  2. Converting image to a byte stream:
  3. Generating random number usign java script:

2 Comments

  • I also do the same way (store in Session) but I don't store Bitmap. Here is my solution:

    - Create a temp file (by using System.IO.Path.GetTempFileName())
    - Save uploaded file to temp file, store tem file name in Session
    - Use Handler to point to temp file. For example: UploadImageHandler.ashx?f=[temp file name]
    - In ProcessRequest, we can read data from temp file and sent it to client by Bitmap.Save() function.
    - When the Session is End, delete the temp file

    Above solution is better because i use a little memory. Most of web server is shared, we should not use too much memory or the whole system will down.

  • Yes, it consumes little memory. But it introduces another problem. It is ‘Cleaning temporary files’. It is not a pain as the use of those images is instantaneous. So we can delete any files older than 1 min prior to save a file in the temp location.

    But, actually this idea is only for the people who don’t want to save temporary files in the disk. This question has been asked several times in the asp.net forum.

Comments have been disabled for this content.