|
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
{
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()
{
}
for the progress bar page to keep track on the progress
private object TotalSize
{
set
{
rwl.AcquireWriterLock(1000);
try
{
if (value==null)
HttpContext.Current.Application.Remove(
contentLengthKey);
HttpContext.Current.Application[contentLengthKey]=value;
}
finally
{
rwl.ReleaseWriterLock();
}
}
}
public static object GetTotalSize(string guid)
{
ReaderWriterLock srwl = new ReaderWriterLock();
try
{
srwl.AcquireReaderLock(1000);
return HttpContext.Current.Application[guid +
"uploadlength"];
}
finally
{
srwl.ReleaseReaderLock();
}
}
public static object GetCurrentSize(string guid)
{
ReaderWriterLock srwl = new ReaderWriterLock();
try
{
srwl.AcquireReaderLock(1000);
return HttpContext.Current.Application[guid +
"uploadprogress"];
}
finally
{
srwl.ReleaseReaderLock();
}
}
private object CurrentSize
{
set
{
rwl.AcquireWriterLock(1000);
try
{
if (value==null)
HttpContext.Current.Application.Remove(uploadKey);
HttpContext.Current.Application[uploadKey] =value;
}
finally
{
rwl.ReleaseWriterLock();
}
}
}
public object Create(object parent,object configContext,
XmlNode section)
{
if (section != null)
{
HttpContext.Current.Application =
Int32.Parse(section.SelectSingleNode("@bufferSize").Value);
HttpContext.Current.Application[pagesKey] =
section.SelectSingleNode("@pages").Value.Split(',');
}
else
{
HttpContext.Current.Application = 1024;
HttpContext.Current.Application[pagesKey] = new string[]{""};
}
return null;
}
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);
uploadKey = app.Context.Request.QueryString["guid"] +
"uploadprogress";
contentLengthKey = app.Context.Request.QueryString["guid"] +
"uploadlength";
rwl = new ReaderWriterLock();
int bufferSize = (int)app.Application;
if (IsUploadPages())
{
if (app.Context.Request.ContentLength > 0)
{
TotalSize = app.Context.Request.ContentLength;
MemoryStream mem = new MemoryStream(
app.Context.Request.ContentLength);
byte [] data = worker.GetPreloadedEntityBody();
mem.Write(data, 0, data.Length);
int read = 0;
int counter = data.Length;
while (counter < app.Context.Request.ContentLength)
{
if (counter + bufferSize >
app.Context.Request.ContentLength)
bufferSize = app.Context.Request.ContentLength
- counter;
data = new byte;
CurrentSize = counter;
read = worker.ReadEntityBody(data, bufferSize);
counter += read;
mem.Write(data, 0, bufferSize);
}
mem.Position = 0;
byte [] memData = new byte[mem.Length];
mem.Read(memData, 0, (int)mem.Length);
PushRequestToIIS(worker, memData);
}
}
}
private void context_EndRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
string [] uploadPages = (string [])app.Application[pagesKey];
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];
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.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 121 (Total in Forum: 121) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Is there a way to make the text property of FileUpload Control readonly
modified on Wednesday, August 13, 2008 1:37 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Sadly this control won't work in Medium Trust which is typically in operation on shared servers (for good reason).
Basically the reflection that is involved in the GetWorkerRequest() and PushRequestToIIS() will break things.
Additionally, the Worker Request might NOT be an IsapiWorkerRequest. On my host it is an IIS7WorkerRequest which works very differently internally! ... I tried another solution using Request.Filter but that didn't get called until the file had completely uploaded.
So currently, I'm not sure if this is possible until I've moved to a dedicated server.
-- If the distro you're using isn't listed above; you can probably work it out yourself 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
I'm wondering what will happen to non-upload requests? Since the PushRequestToIIS() only seems to be called for upload pages. Or does asp.net know whether the request has been read yet?
"For fifty bucks I'd put my face in their soup and blow." - George Costanza CP article: SmartPager - a Flickr-style pager control with go-to-page popup layer.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
That's the purpose of IsUploadPages(). It checks whether the current page is one that handles uploads, and if not then it leaves the request alone. If it is, then it intercepts the upload and passes it on when its done...
-- If the distro you're using isn't listed above; you can probably work it out yourself 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
when i run this script i found the following ERROR can any body help that how to resolve it the error is
The type name 'WebForm1' does not exist in the type 'System.Web.UI.WebControls.FileUpload'
Line 112: public webform1_aspx() { Line 113: string[] dependencies; Line 114: ((FileUpload.WebForm1)(this)).AppRelativeVirtualPath = "~/webform1.aspx"; Line 115: if ((global::ASP.webform1_aspx.@__initialized == false)) { Line 116: dependencies = new string[1];
waiting for reply
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I also got that error after I converted the solution to VS2005 format. Any one have idea about this error ?
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Hi this is nice article.. i have one doubt ... i converted this project for vs 2005. I can't get where GUID is assigned
r_palanivel83 10:01 4 Jan '06
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi,
some errors occurred while I was trying to import the project into VS2005... Is there anybody can provide an "updated" version ? 
I think that would be useful for many people...
Thanks, HyP
|
| Sign In·View Thread·PermaLink | 2.67/5 (3 votes) |
|
|
|
 |
|
|
Hi, I also tried to convert this project to VS2005 but getting error. Is this code available in vs2005 Please hepl me out its argent to me.
Thanks, VP
|
| Sign In·View Thread·PermaLink | 1.50/5 (2 votes) |
|
|
|
 |
|
|
Hi
I would like to create the file upload controls dynamically using javascript (like when user clicks "Add another file" a new HTML INPUT FILE control should appear) and utilize the same code to upload the files.
Any help would be great.
Thanks, Ash.
|
| Sign In·View Thread·PermaLink | 2.00/5 (2 votes) |
|
|
|
 |
|
|
I made some minor tweaks (mentioned below), but the important http module code is mostly the same. Works great!
My tweaks:
The progress bar user control isn't really necessary. I just added a div in place of the progress bar user control and gave it a different background color than the containing table cell, and then set the width of the div to the percent that is calculated on each refresh. That way the displayed progress bar reflects the uploaded percentage more accurately and it just seems a little more straight forward to me.
Still, that was a minor change to how it's displayed, the Upload Module is where the real work is done, thanks for that.
-- modified at 14:39 Friday 19th October, 2007
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Hi, Im new for asp.net, can u plz explain me the above code briefly, but i want this same solution. plz reply, im awaiting 4 ur favourable reply...
thanks a lot........
SUNDAR
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi, Thanks for the article, this technique seems my best option to implement an upload progress bar.
I started rewriting your code a little, since I wanted to store additional information about the upload; such as a status flag so I know when it's finished (you used Session for that purpose in the Progress Bar page itself, whereas I want an IHttpHandler to return the progress value, so my progress bar can sit on the originating page, with the upload taking place in an IFrame; Session isn't available in IHttpHandler so it's back to Application).
I'm concerned about your use of the ReaderWriterLock; from my understanding, in order for the Lock to work, all threads must be accessing the same *instance* of the ReaderWriterLock. Since you are just instancing a brand new one (in GetTotalSize, GetCurrentSize, and at the beginning of each request) there will be nothing for it to lock? I do not see how this protects the Application object in any way, since any other threads accessing it will be using different instances as well, since they will most likely be running in a different request.
This usage of locking seems redundant, perhaps you can correct me if I'm wrong about what is happening here?
When I've finished my rework of the code I'm happy to post it up on CodeProject for anyone else to use.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
Yes, I agree, the ReaderWriterLock needs to be static and the same instance must be used in all methods.....However, the more I look at it, I'm starting to wonder if we even need a locking mechanism?
The way I did it, my upload page generates a unique id for each upload, so no two threads should ever request information about the same upload, I believe this is what the article assumes as well....however, the ReaderWriterLock as it's written, will prevent multiple uploads from accessing the Application object, even though they would be reading and writing information about different uploads.
So, if I open a browser and upload a file, a unique uploadID will be generated and all information stored in the Application object's collection will use the uploadID in it's key. For the sake of this example, let's say the first uploadID is 1. Now, let's say another user uploads a file immediately after the first upload has started, their uploadID is 2. As the UploadModule is currently written, it does not allow upload 2 to read or write to the Application object's collection if upload 1 is already reading or writing to the Application object (and vice versa).
But each upload's information is stored as a separate element in the Application object's collection. Upload 1 would be reading and writing to elements with keys of "1_uploadlength" and "1_uploadprogress", while upload 2 would be reading and writing to elements with keys of "2_uploadlength" and "2_uploadprogress". So there's no reason upload 2 should have to wait to write to "2_uploadprogress" while upload 1 is writing to "1_uploadprogress", because they are separate elements.
It'd be like having 5 cars needing to cross a bridge that's 5 lanes wide, with each car relegated to their own lane, but only allowing 1 car to cross at a time...there's no need to put that restriction there, since 1 car crossing would have no impact on whether another car should cross.....
But anyway, I believe the Application object already uses its own ReaderWriterLock behind the scenes so this point may be moot. However, if the Application object already uses it's own ReaderWriterLock, there's no need for us to do so.
I'm going to play around with this a bit more...
-- modified at 19:31 Monday 22nd October, 2007
|
| Sign In·View Thread·PermaLink | 5.00/5 (2 votes) |
|
|
|
 |
|
|
I'd be tempted to lock the Application at any point where you're adding or removing an element - at this point I imagine there could be changes in the underlying data of the state collection. As for what on earth else dot net might be doing behind the scenes, who can really know?
Also in my version I used the simpler lock(Application){ ... } construct (which gets around any issues of having to manage a static instance); I don't think it's a particularly expensive operation, - and if it proved too expensive, you could simply reduce the polling interval. -- If the distro you're using isn't listed above; you can probably work it out yourself  <Serializer/>
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I had the lock in there originally but since there's no shared element in the Application collection being modified, it doesn't seem necessary. Now, if we were keeping track of the total number of uploads, or something where each thread accessed the same specific element, we would need a lock.
However, after reading this: http://www.odetocode.com/Articles/83.aspx I'm not sure if we need to explicitly create the lock or not. That article used a decompiler to look at the HttpApplicationState code, and in the Set and Get it appears that the HttpApplicationState uses it's own ReaderWriterLock to set and get values. MSDN documentation also states that the HttpApplicationState is "safe for multi-threaded operations"...
So I'm thinking that adding an additional lock is, like you said, redundant.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
That wasn't my point. The way you've used the RWL wouldn't have locked anything anyway. You need to share one instance globally, but here you are creating a new local instance every time.
-- If the distro you're using isn't listed above; you can probably work it out yourself 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Been a while since I looked at this, so I'm not sure what you mean, but as I mentioned in the above post, I'm explicitly NOT locking anything, since HttpApplicationState already uses its own internal RWL and each upload is independent of the other.
But I did say, in my initial posting (on Oct 22 07), that if I was going to use a RWL, it'd need to be static, and therefore shared globally...
Unless....maybe you think I'm the author of the article? I'm not...but do agree that in the author's implementation, the RWL doesn't do anything...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
My bad, it had also been a while since I looked at this... yes I'd confused you with the original author.
In my uploader I ended up using the database to persist status and progress. Possibly marginally less efficient, but allowed me a greater degree of control. Still useful to know that HttpApplicationState is thread-safe...
Now, I never got around the "medium trust" issue that prevents this code running:
return (HttpWorkerRequest)provider.GetService( typeof(HttpWorkerRequest));
I tried inserting a filter into Request.Filters, but it didn't get executed until the entire file had uploaded.
Is there any other way of getting the worker request, without using that provider.GetService method, I wonder?
-- If the distro you're using isn't listed above; you can probably work it out yourself 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi, I converted this to .net2005 using the wizard. Then when i saw the web.config there is an attribute
but my file name is uploadwallpaper.aspx and can u please tell me how u did it ... is fileupload is the namespace or virtual directory also if i want to use this in multiple pages how do I specify the page names. Thanks in advance Regards Meher
|
| Sign In·View Thread·PermaLink | 3.25/5 (4 votes) |
|
|
|
 |
|
|
You are using http://fileupload.localhost/....
Please tell me how to Create Subdomains in IIS.Thanks in Advance.
Dileep.M
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
| | |