ASP.NET File Upload with *Real-Time* Progress Bar
I had a number of people email me asking how the ProgressBar Toolkit control I blogged about last month could be used to provide progress for file uploads. So I thought I would spend some time and see how this could be done. Thankfully, there are a ton of resources available on the internet that discuss this in pretty good detail. But I didn't see any good examples of how to include the real-time count of the number of bytes that have been transferred. So I thought I would try to tackle that problem while building my example.
Update 7/27/2008: This post is bogus - I have over simplified the problem. My goal was to display the number of bytes that have been transferred to the web server, not the number that have been saved to disk. As atashbahar pointed out, this will require a little bit more work that what I have shown. I followed atashbahar's suggestion and downloaded the NeatUpload control (its free) and started taking a look at its HttpModule. I am going to take a look at how the NeatUpload handles uploading large files and will update this post or create a part II that outlines what I have found.
Also, a couple of quick notes on the demo. The first is that the demo looks like crap in FF, sorry but I gave up styling the input element. I will come back to it later. Second, I have a limited amount of bandwidth that I can use each month, so I don't accept files larger than 250 bytes. I recommend creating a real small text file and using that to upload to the demo site. I have setup the demo to upload only a handful of bytes per second, so you will be able to get a feel for how the progress bar works even with real small files.
A few minutes of googleing and you will learn that creating the file upload widget above includes the following pieces ...
- A input element for collecting the file. I am using the ASP.NET FileUpload control to render this.
- 2 ASP.NET pages. One contains the FileUpload control and has codebehind logic that reads the file from the response and writes it some place. And a second page that contains an IFRAME with the src attribute that points to the upload page.
- A UI widget for displaying the upload's progress. I have used the progress bar control I have blogged about previously
Here is the markup for the Default page
And here is the Upload page
Posting the File - Building the Upload.aspx Page
This is the complicated part of this implementation. When the Upload.aspx page is submitted, I want to take the uploaded file off of the response and save it to disk. But, because I want to display the real-time progress, I need a way to get at how much of the uploaded file has been transferred. In my first crack at solving this, I tried just calling SaveAs on the HttpPostedFile object and then using a regular WebMethod that would return the size of the file on disk. Something like this ...
But, that wasn't working too well because the file was being buffered as it was being written to disk so my progress bar would jump from 0 to 67 to 100. Which wasn't what I wanted. So to get around this, I decided that I would try handling saving the file myself. To do this I manually read X bytes off the response stream and write it to a FileStream (where X is the size of the buffer). For the demo I have set X to 1, so each byte is read/written individually, but for non-demo purposes I believe I will be updating the size of the buffer based on the size of the file being uploaded. The smaller the buffer size, the longer the file takes to upload, but the more accurate our progress bar is. And of course, a large buffer will upload faster, but our progress bar won't be as accurate.
Checking the Progress
Back on the main page (Default.aspx), I have setup a Page Method that retrieves the number of bytes that have been written to the FileStream as well as a friendly status message that reports the total number of bytes that have been transferred so far.
I am not using this in production (yet!). So if you find some issues with it, just leave a comment or drop me an email. And there is still a bunch that can be approved (like supporting multiple files), so if you make any mods to this, let me know that too so I don't have to redo any of your hard work ;)
That's it. Enjoy!