zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    JS pop push unshift shift的作用与区别
    白话js this指向问题
    JS作用域,作用域,作用链详解
    angular $q promise详解
    白话$resource,$resource中的增删改查
    ng-app与ng-app='myApp'的区别
    详解 高效字节流
    详解 字节输出流 与 字节输入流
    阶段性复习与应用——复制多级文件夹
    详解 字节流
  • 原文地址:https://www.cnblogs.com/cy163/p/280839.html
Copyright © 2011-2022 走看看