• Server Side File Upload Progress Bar


    To test the application, please type http://localhost/fileupload/webform1.aspx?guid=123abc. It is recommended that you test with files that is over 20MB to see the effects. During testing, please use two machines to test - one as a server and one as a client. When testing on the same machine, the internet explorer tends to hang up during upload because the ASP.NET process has taken all the CPU power from the internet explorer, preventing it from refreshing the progress status in the window.

    Introduction

    There are many ASP.NET file upload progress bars flowing around, but I have come across many of them that don't work. This code is referenced by the article in http://krystalware.com/blog/archive/2004/10/11/303.aspx. It works. However, that one provides more functions then what I want, so I spent time to go through his code and extract some important bits of his code and build this file upload progress bar.

    public sealed class UploadModule : IHttpModule, IConfigurationSectionHandler
        {
            //the name of the configuration section the the web.config
            private const string configSection = "UploadModuleManagement";
            private const string bufferSizeKey = "uploadBufferSize";
            private const string pagesKey = "uploadPages";
            private string uploadKey;
            private string contentLengthKey;
            private ReaderWriterLock rwl;
            
            public void Dispose()
            {
    
            }
            
            //set the total size in the application object (not session object)
            for the progress bar page to keep track on the progress
            private object TotalSize
            {
                set
                {
                    //must use a lock to update the application object 
                    rwl.AcquireWriterLock(1000);
                    try
                    {
                        if (value==null)
                            HttpContext.Current.Application.Remove(
                              contentLengthKey);
                        HttpContext.Current.Application[contentLengthKey]=value;
                    }
                    finally
                    {
                        rwl.ReleaseWriterLock();
                    }
                }
            }
    
            //visible by the progress bar page
            public static object GetTotalSize(string guid)
            {
                ReaderWriterLock srwl = new ReaderWriterLock();
                try
                {
                    srwl.AcquireReaderLock(1000);
                    return HttpContext.Current.Application[guid + 
                      "uploadlength"];
                }
                finally
                {
                    srwl.ReleaseReaderLock();
                }
    
            }
    
            //visible by the progress bar page
            public static object GetCurrentSize(string guid)
            {
                ReaderWriterLock srwl = new ReaderWriterLock();
                try
                {
                    srwl.AcquireReaderLock(1000);
                    return HttpContext.Current.Application[guid + 
                      "uploadprogress"];
                }
                finally
                {
                    srwl.ReleaseReaderLock();
                }
    
            }
    
           //visible by the progress bar page
            private object CurrentSize
            {
                set
                {
                    rwl.AcquireWriterLock(1000);
                    try
                    {
                        if (value==null)
                            HttpContext.Current.Application.Remove(uploadKey);
                        HttpContext.Current.Application[uploadKey] =value;
                    }
                    finally
                    {
                        rwl.ReleaseWriterLock();
                    }
                }
            }
            
    
            //read from config section
            public object Create(object parent,object configContext, 
                XmlNode section)
            {
                if (section != null) 
                {
                    HttpContext.Current.Application[bufferSizeKey] = 
                      Int32.Parse(section.SelectSingleNode("@bufferSize").Value);
                    HttpContext.Current.Application[pagesKey] = 
                      section.SelectSingleNode("@pages").Value.Split(',');
                }
                else
                {
                    HttpContext.Current.Application[bufferSizeKey] = 1024;
                    HttpContext.Current.Application[pagesKey] = new string[]{""};
                }
                return null;
            }
    
            //check whether the page that is processing 
            //is belonged to an "upload page". 
            private bool IsUploadPages()
            {
                HttpApplication app =  HttpContext.Current.ApplicationInstance;
                string [] uploadPages = (string [])app.Application[pagesKey];
                for (int i = 0; i<uploadPages.Length ;i++)
                {
                    if ( uploadPages[i].ToLower() ==
                      app.Request.Path.Substring(1).ToLower())
                        return true;
                }
                return false;
            }
    
            public void Init(HttpApplication app)
            {
                ConfigurationSettings.GetConfig(configSection);
                app.BeginRequest += new EventHandler(context_BeginRequest);
                app.Error += new EventHandler(context_Error);
                app.EndRequest += new EventHandler(context_EndRequest);
            }
    
            private void context_BeginRequest(object sender, EventArgs e)
            {
                
                HttpApplication app = sender as HttpApplication;
                HttpWorkerRequest worker = GetWorkerRequest(app.Context);
                //get the querystring, must not use Reques.Params["guid"]
                uploadKey = app.Context.Request.QueryString["guid"] + 
                  "uploadprogress";
                contentLengthKey = app.Context.Request.QueryString["guid"] + 
                  "uploadlength";
                rwl = new ReaderWriterLock();
    
               //the number of bytes get from the client everytime  
               int bufferSize = (int)app.Application[bufferSizeKey];
                //check whether the page is an upload page
                if (IsUploadPages())
                {
                    if (app.Context.Request.ContentLength > 0)
                    {
                        TotalSize = app.Context.Request.ContentLength;
                        MemoryStream mem = new MemoryStream(
                          app.Context.Request.ContentLength);
                        //read the first portion of data from the client
                        byte [] data = worker.GetPreloadedEntityBody();
                        mem.Write(data,  0, data.Length);
    
                        int read = 0;
                        int counter = data.Length;
                        //keep reading if the first 
                        //read cannot read all the data        
                        while (counter < app.Context.Request.ContentLength)
                        {
                            if (counter + bufferSize > 
                              app.Context.Request.ContentLength)
                                bufferSize = app.Context.Request.ContentLength 
                                - counter;
                            data = new byte[bufferSize];
                            CurrentSize = counter;
                            read = worker.ReadEntityBody(data, bufferSize);
                            counter += read;
                            mem.Write(data,  0, bufferSize);
                        }
                    
                        mem.Position = 0;
                        //push all the data to memory stream
                        byte [] memData  = new byte[mem.Length];
                        mem.Read(memData, 0, (int)mem.Length);
                        //finishing the interception, push all 
                        //the data to the worker process again
                        PushRequestToIIS(worker, memData);
                    }
                }
            }
    
            private void context_EndRequest(object sender, EventArgs e)
            {
                HttpApplication app = sender as HttpApplication;
                string [] uploadPages = (string [])app.Application[pagesKey];
                //check whether the page is an upload page and the application 
                //object to null, so that the progress 
                //bar page knows the upload is finished
                if (IsUploadPages())
                {
                    TotalSize  = null;
                    CurrentSize = null;
                }
            }
    
            private void context_Error(object sender, EventArgs e)
            {
                HttpApplication app = sender as HttpApplication;
                string [] uploadPages = (string [])app.Application[pagesKey];
                //check whether the page is an upload page
                if (IsUploadPages())
                {
                    TotalSize  = null;
                    CurrentSize = null;
                }
            }
    
    
            HttpWorkerRequest GetWorkerRequest(HttpContext context)
            {
                IServiceProvider provider = (IServiceProvider)
                        HttpContext.Current;
                return (HttpWorkerRequest)provider.GetService(
                         typeof(HttpWorkerRequest));
            }
    
            private void PushRequestToIIS(HttpWorkerRequest request, 
                byte[] textParts)
            {
                BindingFlags bindingFlags = BindingFlags.Instance 
                          | BindingFlags.NonPublic; 
                Type type = request.GetType();
                while ((type != null) && (type.FullName != 
                     "System.Web.Hosting.ISAPIWorkerRequest"))
                    type = type.BaseType; 
    
                if (type != null)
                {
                    type.GetField("_contentAvailLength", 
                      bindingFlags).SetValue(request, textParts.Length); 
                    type.GetField("_contentTotalLength", 
                      bindingFlags).SetValue(request, textParts.Length);
                    type.GetField("_preloadedContent", 
                      bindingFlags).SetValue(request, textParts); 
                    type.GetField("_preloadedContentRead", 
                      bindingFlags).SetValue(request, true);
                }
            }
        }

    The interception is fulfilled by extracting the HttpWorkerRequest in the GetWorkerRequest method. I have done some research on that code in this regard. It is quite powerful. You can probably do more research yourself to find out more.

    I cannot used Session to store the percentage of upload because the Session object in the ASP.NET process has not been initialized. Instead, I use the Application object instead.

    The core part of the method is worker.GetPreloadedEntityBody() and worker.ReadEntityBody(data, bufferSize). These two methods read the data from the client machine. After all the data is read, they are stored in the Memory Stream. The final step is to push the data into ISAPIWorkerRequest. These ideas are coming from the slick upload component in http://krystalware.com/blog/archive/2004/10/11/303.aspx. The slick upload even supports saving the uploading file contents in the disk, without consuming the .NET memory stream during upload. Worth to have a look. Instead of calling my component as a "File upload progress bar", I should call it "MIME upload progress bar".

    The web config is configured in the following way -

    <configSections>
            <section name="UploadModuleManagement" 
             type="UploadModule.UploadModule, UploadModule" />
        </configSections>
        <UploadModuleManagement bufferSize="1024" 
          pages="fileupload/webform1.aspx" />
    

    Where "fileupload/webform1.aspx" is your page that performs the upload. The buffersize is the number of bytes that is read from the client.

    About benoityip


    I have been in programming for some years. Though I am not a genius in programming, I am interested in it.
    Other than computing, I am interested in buddism as well. It talks about true happiness in our mind, everything is creating from the mind, ouselves. I am the creator.

    Click here to view benoityip's online profile.

  • 相关阅读:
    linux常用命令
    Python 父类调用子类方法
    import win32api 安装pip install pypiwin32
    Python 封装DTU-215码流卡 第一天
    git apply -v 提示 Skipped patch 打不上patch的解决办法
    2019/10/29
    12/9/2019
    11/9/2019
    9/7/2019
    人生若有命中注定
  • 原文地址:https://www.cnblogs.com/cy163/p/280839.html
Copyright © 2020-2023  润新知