Classic ASP File Upload: A Complete Step-by-Step Tutorial
Everything you need to accept a file from a browser in Classic ASP, from the form markup to saving the file on disk.
Why file upload is harder in Classic ASP than it looks
Every other form field in Classic ASP arrives in Request.Form. A file does not.
The moment you add enctype="multipart/form-data" to a form — which you must, because
without it the browser sends only the file name, not its bytes — the ASP runtime stops
parsing the request body for you. Request.Form comes back empty, including for the plain
text fields that used to work.
That single design decision is the reason "how to upload a file in ASP" has never had a one-line answer.
The raw body is still available through Request.BinaryRead, but it hands you an untyped
byte array containing MIME boundaries, per-part headers, and the file contents interleaved.
Turning that into a saved file is your job.
Step 1: the HTML form
Three things matter here and nothing else does:
<form id="form1" method="POST" enctype="multipart/form-data">
<input type="file" name="myfile" />
<input type="submit" value="Upload" />
</form>
method="POST"— a GET form cannot carry a file body.enctype="multipart/form-data"— without it you receive the file name as a string and nothing else. This is the single most common cause of "my upload does nothing".type="file"with aname— the name is how you find the part on the server.
Step 2: what actually arrives on the server
It helps to see the request body once. A single-file post looks roughly like this:
Content-Type: multipart/form-data; boundary=---------------------------7d5a2
-----------------------------7d5a2
Content-Disposition: form-data; name="myfile"; filename="photo.jpg"
Content-Type: image/jpeg
<raw binary bytes of photo.jpg>
-----------------------------7d5a2--
To save photo.jpg you have to read the boundary token out of the
Content-Type header, scan the body for it, parse the part headers, find the blank line
that separates headers from content, and copy every byte between there and the next boundary
— without letting VBScript's string functions mangle the binary along the way.
Step 3 (the hard way): Request.BinaryRead
A minimal hand-rolled parser looks like this. It works, and it is worth reading once so you know what a component is doing for you:
<%
Dim byteCount, binData
byteCount = Request.TotalBytes
binData = Request.BinaryRead(byteCount) ' entire upload, in memory
' RSTRING conversion so we can use InStrB / MidB on the bytes
Dim rawText
rawText = BytesToString(binData)
' 1. pull the boundary out of the content type header
Dim boundary
boundary = Request.ServerVariables("HTTP_CONTENT_TYPE")
boundary = Mid(boundary, InStr(boundary, "boundary=") + 9)
' 2. locate the part, its filename, and the start of the binary payload
' 3. MidB() out the bytes between the headers and the closing boundary
' 4. write them with an ADODB.Stream
%>
The catch: Request.BinaryRead loads the whole post into
server memory before your first line of code runs. A 500 MB upload needs 500 MB of RAM in the
IIS worker process, plus another copy while you slice it. This approach is fine for a 200 KB
avatar and completely unusable for anything large. See
uploading large files in Classic ASP for what to do instead.
Step 4 (the old way): a COM component
For years the standard answer was to install a compiled component — ASPUpload, SA-FileUp,
AspSmartUpload — and let it parse the stream. That works well, but it needs
regsvr32 on the server, which means:
- You need administrator access to the box. Most shared hosts will not do it.
- Moving the site to a new server means re-registering, and remembering that you had to.
- Licences are usually per-server, so a staging box costs extra.
Pure ASP file upload without components covers the alternative in detail.
Step 5 (the practical way): a pure ASP upload script
ASP Uploader is written entirely in Classic ASP and JavaScript. You copy a folder into your site, include one file, and instantiate the object. There is nothing to register and nothing for the visitor to install.
<%@ Language="VBScript" %>
<!-- #include file="aspuploader/include_aspuploader.asp" -->
<html>
<body>
<form id="form1" method="POST">
<%
Dim uploader
Set uploader = new AspUploader
uploader.Name = "myuploader"
uploader.MaxSizeKB = 10240 ' 10 MB ceiling
uploader.InsertText = "Upload File (Max 10M)"
uploader.AllowedFileExtensions = "*.jpg,*.png,*.gif,*.zip"
%>
<%= uploader.GetString() %>
</form>
</body>
</html>
Note what is not there: no enctype, no <input type="file">.
GetString() renders the button, the queue, and the progress bar, and the file travels
on its own background request rather than as part of your form post.
Reading the uploaded file after the post
When the form posts back, the field named after your uploader contains a "/"-separated list of GUIDs, one per uploaded file. Exchange each GUID for a file object:
<%
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))
Response.Write mvcfile.FileName & " (" & mvcfile.FileSize & " bytes)<br/>"
' keep it:
mvcfile.MoveTo Server.MapPath("uploads/" & mvcfile.FileName)
Next
End If
%>
The important members are FileName, FileSize,
MoveTo (move out of the temp folder) and CopyTo (keep the temp copy).
Anything you do not move is cleaned up automatically.
Saving straight to a folder
If you do not need to inspect the files at all, set SaveDirectory and the script writes
them for you as they arrive:
<%
Set uploader = new AspUploader
uploader.Name = "myuploader"
uploader.MultipleFilesUpload = true
uploader.SaveDirectory = "savefiles" ' relative to the current page
uploader.Render()
%>
Step 6: the server-side checklist before you go live
- Write permission. The IIS application pool identity (usually
IIS AppPool\YourPoolName) needs Modify on the upload folder. Nothing else does. - No execute permission. Set the upload folder's handler mapping to none so a
stored
.aspfile can never run. This is not optional. - Validate on the server, not just the browser. Client-side checks are a usability feature; an attacker skips them entirely. See validating file size and type.
- Never trust the supplied file name. Strip paths, strip
.., and prefer a name you generate. See the security checklist. - Raise the IIS limits if you accept big files.
maxRequestEntityAllowedanduploadReadAheadSizeboth cap uploads long before your code sees them.
Frequently asked questions
Why is Request.Form empty when I add a file input?
Because the form is now multipart/form-data and Classic ASP does not parse that
encoding. Every field — text fields included — has to be read out of the raw body.
A component or upload script gives them back to you.
Can Classic ASP upload files without a component?
Yes. Either parse Request.BinaryRead yourself, or use a pure ASP upload script such as
ASP Uploader, which is plain .asp files you copy into the site.
What is the maximum file size Classic ASP can accept?
By default IIS caps a request body at 200 KB (maxRequestEntityAllowed). Raising it lets
you go much higher, but an in-memory BinaryRead will still exhaust the worker process
on large files. Chunked uploading removes the ceiling entirely.
Do visitors need Flash, Silverlight or a browser plug-in?
No. Modern browsers select and upload multiple files natively through the HTML5 File API.
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.