Adding a Real-Time Upload Progress Bar to Classic ASP
Why Response.Flush cannot do it, and what actually reports bytes-transferred to the browser.
Why you cannot just Response.Flush a percentage
The intuitive approach — write a bit of HTML, flush, repeat — cannot work for uploads, and it is worth understanding exactly why before you spend an afternoon on it.
An ASP page does not begin executing until IIS has received the entire request body. By the time your first line of VBScript runs, the upload is already finished. There is no point during the transfer at which your script is alive to report anything.
Response.Flush reports progress on downloads and long-running
server-side work. It cannot report progress on an upload, because the upload has completed before
the script starts.
How a real progress bar works
Progress has to come from somewhere that is alive during the transfer. There are two such places, and modern uploaders use the first:
1. The browser (the good way)
The upload is sent by JavaScript through XMLHttpRequest, which fires a
progress event as bytes leave the machine:
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
var pct = Math.round((e.loaded / e.total) * 100);
document.getElementById("bar").style.width = pct + "%";
}
};
xhr.open("POST", "upload-handler.asp");
xhr.send(formData);
No server round-trip, updates as often as the browser can produce them, and it works when the server is a single box or a farm. The one caveat: it measures bytes that have left the browser, which on a fast local network can reach 100% slightly before the server has finished writing the file. That is why a good uploader shows a "processing" state after the bar fills rather than declaring success.
2. A server-side progress handler (the fallback)
A second, separate ASP page is polled every second or so and reports how many bytes the receiving
handler has written so far. This is how progress worked before XMLHttpRequest upload
events existed, and it is still needed for older environments.
On a web farm, the polling request must reach the same server that is receiving the upload, or it will report 0% forever. Sticky sessions or a shared progress store are required. Client-side progress has no such problem.
Progress with ASP Uploader
The progress bar is rendered and driven for you; you configure its appearance rather than its mechanics.
<%
Dim uploader
Set uploader = new AspUploader
uploader.Name = "myuploader"
uploader.MultipleFilesUpload = true
uploader.InsertText = "Select files"
uploader.ShowProgressBar = true ' the bar itself
uploader.ShowProgressInfo = true ' "3.2 MB of 12 MB, 45 seconds left"
uploader.ProgressBarHeight = "18px"
uploader.ProgressPanelWidth = "360px"
uploader.UploadingMsg = "Uploading, please wait..."
uploader.UploadProcessingMsg = "Processing the file on the server..."
%>
<%= uploader.GetString() %>
With MultipleFilesUpload on, each queued file gets its own row and its own status while
the panel shows overall progress. Users can cancel a single file or the whole queue:
uploader.CancelUploadMsg = "Cancel this upload"
uploader.CancelAllMsg = "Cancel all uploads"
uploader.NumFilesShowCancelAll = 3 ' show "cancel all" once 3+ files are queued
Reacting to progress in your own JavaScript
If you want to disable a submit button while files are moving, or show your own status text, hook the client-side events:
<script type="text/javascript">
function CuteWebUI_AjaxUploader_OnPostback() {
// every queued file has finished - safe to submit the form
document.forms[0].submit();
}
function CuteWebUI_AjaxUploader_OnTaskComplete(task) {
// one file finished; task.FileName, task.FileSize are available
console.log("done: " + task.FileName);
}
</script>
Restyling the bar
The rendered markup is plain HTML, so CSS is all you need. The progress fill is
.uploaderprogressleft inside .uploaderprogress:
.uploaderprogress {
background: #eef1f7;
border-radius: 999px;
overflow: hidden;
}
.uploaderprogressleft {
background: linear-gradient(90deg, #377dff, #00c9a7);
}
The fill width is set in pixels, derived from the panel width. Do not override it with a percentage width in your own CSS — the bar will then report the wrong position.
Small things that make progress feel honest
- Show bytes, not just a percentage. "48 MB of 120 MB" tells the user the transfer is real; a percentage alone reads as a guess.
- Show a distinct processing state. The gap between "bytes sent" and "server done" is where users assume it has frozen and hit refresh.
- Always offer cancel. A progress bar with no exit is worse than no progress bar.
- Never show a fake animation. An indeterminate spinner is more honest than a bar that crawls on a timer.
Frequently asked questions
Can I show upload progress in Classic ASP without JavaScript?
No. The ASP page does not run until the upload has fully arrived, so progress must be measured in the browser or by a separate polled request.
Why does my progress bar jump straight to 100%?
Usually the file is small enough to be sent in one burst, or you are on localhost. It can also mean the bar is being driven by request completion rather than by real progress events.
Does the progress bar work behind a reverse proxy?
Client-side progress does, because it measures bytes leaving the browser. A polled server-side progress handler needs the poll to reach the same server that is receiving the upload.
Skip the plumbing
ASP Uploader does everything on this page out of the box: multi-file selection, a real progress bar, client and server validation, chunked large-file transfer, and no COM component to register. Drop the folder on your server and add one include line.