Chunked and Resumable File Upload in Classic ASP
Split the file in the browser — what it buys you, and what ASP Uploader does instead.
The problem chunking solves
A normal upload is one HTTP request carrying one file. That single fact is the root of nearly every Classic ASP upload limitation:
Request.BinaryReadbuffers the entire body in the worker process, so a 2 GB file needs 2 GB of RAM — several times that once you parse it.- The request must fit under
maxRequestEntityAllowed(2 GB hard ceiling) andmaxAllowedContentLength. - The request must complete inside
Server.ScriptTimeoutand the connection timeout. - A dropped connection at 98% loses everything. There is nothing to resume from.
Chunking attacks all four at once by changing the shape of the transfer rather than the limits.
How chunked upload works
The browser slices the file and sends the pieces as separate requests. The server appends each one to a temporary file and immediately forgets it.
var CHUNK = 4 * 1024 * 1024; // 4 MB
var offset = 0;
function sendNext(file, uploadId) {
if (offset >= file.size) { finish(uploadId); return; }
var slice = file.slice(offset, offset + CHUNK); // Blob.slice - no copy of the whole file
var xhr = new XMLHttpRequest();
xhr.open("POST", "chunk-handler.asp?id=" + uploadId + "&offset=" + offset);
xhr.onload = function () {
offset += CHUNK;
setProgress(Math.min(offset, file.size) / file.size);
sendNext(file, uploadId); // one chunk in flight at a time
};
xhr.send(slice);
}
And on the server, each request is small and short-lived:
<%
' chunk-handler.asp - append this piece to the part-file
Dim stm, data, partPath
partPath = Server.MapPath("temp/" & SafeId(Request.QueryString("id")) & ".part")
data = Request.BinaryRead(Request.TotalBytes) ' one chunk only - a few MB
Set stm = Server.CreateObject("ADODB.Stream")
stm.Type = 1
stm.Open
If FileExists(partPath) Then
stm.LoadFromFile partPath
stm.Position = stm.Size ' append
End If
stm.Write data
stm.SaveToFile partPath, 2 ' adSaveCreateOverWrite
stm.Close
%>
What each limit becomes
| Constraint | Single request | Chunked |
|---|---|---|
| Peak server memory | File size × 2–3 | One chunk, regardless of file size |
| Request size limit | Must exceed the file | Must exceed one chunk |
| Script timeout | Must cover the whole transfer | Must cover one chunk |
| Proxy or CDN body cap | Blocks large files outright | Never approached |
| Dropped connection | Start again from zero | Resume from the last chunk |
| Progress reporting | Needs a separate mechanism | Falls out of the chunk count |
Making it resumable
Resume needs one extra thing: a stable identifier for the upload, and a way for the client to ask how much of it the server already has.
<%
' status.asp - how many bytes have we got?
Dim fso, partPath, size
Set fso = Server.CreateObject("Scripting.FileSystemObject")
partPath = Server.MapPath("temp/" & SafeId(Request.QueryString("id")) & ".part")
size = 0
If fso.FileExists(partPath) Then size = fso.GetFile(partPath).Size
Response.ContentType = "application/json"
Response.Write "{""received"":" & size & "}"
%>
The browser asks first, then starts sending from that offset instead of from zero. The identifier has to
survive a page reload, so derive it from something stable — file name, size and last-modified date
— and keep it in localStorage.
SafeId() is not decoration. The id comes from the client and is used to build a file
path, so it must be validated to a known-safe character set before it goes anywhere near
Server.MapPath. Accepting it raw is a path traversal vulnerability. See the
security checklist.
Expiring abandoned parts
Every resumable upload that is never finished leaves a .part file. Sweep them:
<%
Dim fso, folder, f
Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set folder = fso.GetFolder(Server.MapPath("temp"))
For Each f In folder.Files
If LCase(fso.GetExtensionName(f.Name)) = "part" Then
If DateDiff("h", f.DateLastModified, Now()) > 48 Then f.Delete True
End If
Next
%>
Choosing a chunk size
| Chunk size | Effect |
|---|---|
| Under 1 MB | Lots of requests; per-request overhead starts to dominate on fast links |
| 2–8 MB | The usual sweet spot — smooth progress, low memory, cheap retries |
| Over 32 MB | Coarse progress, expensive retries, and you start meeting request limits again |
Match it to the worst connection you must support, not the best. On a flaky mobile link a 32 MB chunk is 32 MB you may have to send twice.
What ASP Uploader does instead
ASP Uploader does not chunk uploads, and it is worth being precise about that, because the difference decides which limits you have to configure.
It sends the file as a single request, and the server reads that request body
incrementally — a loop of small Request.BinaryRead calls appended straight to
a temporary file, rather than one Request.BinaryRead(Request.TotalBytes). You get the memory
benefit of chunking without the complexity:
| Buffer the whole body | ASP Uploader (incremental read) | True chunking | |
|---|---|---|---|
| Peak server memory | File size × 2–3 | One buffer | One chunk |
| IIS request-size limits | Apply | Apply — raise them | Effectively bypassed |
| Resume after a drop | No | No | Yes |
| Code you write | A parser | None | Client and server both |
So if you are accepting large files with ASP Uploader, raise
maxRequestEntityAllowed and maxAllowedContentLength as described in
uploading large files — the memory wall is handled for
you, the request-size ceilings are not. If you need genuine resume-after-failure, that is the
hand-rolled approach earlier on this page.
<%@ Language="VBScript" %>
<!-- #include file="aspuploader/include_aspuploader.asp" -->
<form id="form1" method="POST">
<%
Dim uploader
Set uploader = new AspUploader
uploader.Name = "myuploader"
uploader.MultipleFilesUpload = true
uploader.MaxSizeKB = 4194304 ' 4 GB, rejected in the browser if exceeded
uploader.InsertText = "Select a large file"
%>
<%= uploader.GetString() %>
</form>
<%
If Request.Form("myuploader") & "" <> "" Then
Dim list, i, mvcfile
list = Split(Request.Form("myuploader"), "/")
For i = 0 To UBound(list)
Set mvcfile = uploader.GetUploadedFile(list(i))
mvcfile.MoveTo Server.MapPath("archive/" & mvcfile.FileName)
Next
End If
%>
The IIS settings in uploading large files still matter if you also accept plain form posts, but no single chunked request comes close to them.
Frequently asked questions
What is chunked file upload?
Splitting a file in the browser and sending it as a series of small requests that the server appends to a temporary file, instead of one request carrying the whole file.
Can Classic ASP resume an interrupted upload?
Yes, with chunking. The server reports how many bytes it already holds for a given upload id and the browser continues from that offset.
Does chunking make uploads slower?
Marginally, from per-request overhead — and it is the difference between a 4 GB upload working and failing, so the trade is not close.
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, incremental large-file handling, and no COM component to register. Drop the folder on your server and add one include line.