Uploading Files Alongside Other Form Fields in Classic ASP

The registration form problem: a file input silently empties Request.Form. Here is the fix.

The symptom

You have a registration form that works perfectly. You add a profile-picture field:

<form method="POST" enctype="multipart/form-data">
    <input type="text" name="fullname" />
    <input type="email" name="email" />
    <input type="file" name="photo" />
    <input type="submit" />
</form>

And now every field is empty:

<%
Response.Write Request.Form("fullname")   ' empty
Response.Write Request.Form("email")      ' empty
Response.Write Request.Form.Count         ' 0
%>

Nothing is broken about your text inputs. The enctype is what changed.

Why it happens

Classic ASP parses exactly one request encoding: application/x-www-form-urlencoded. That is what populates Request.Form.

A file input forces multipart/form-data, and ASP does not parse it. The data is all still there in the request body — text fields and file alike, each in its own MIME part — but ASP hands you none of it. The only access is the raw bytes via Request.BinaryRead.

-----------------------------7d5a2
Content-Disposition: form-data; name="fullname"

Ada Lovelace
-----------------------------7d5a2
Content-Disposition: form-data; name="photo"; filename="ada.jpg"
Content-Type: image/jpeg

<binary>
-----------------------------7d5a2--

Do not call Request.BinaryRead after touching Request.Form, or you get ASP 0104 : Operation not Allowed. The body can only be consumed once, one way.

Fix 1: Keep the form urlencoded and upload separately

This is the approach worth reaching for first, because it makes the problem disappear rather than solving it. If the file travels on its own background request, the form never becomes multipart at all — and Request.Form keeps working exactly as it always did.

<%@ Language="VBScript" %>
<!-- #include file="aspuploader/include_aspuploader.asp" -->

<form id="form1" method="POST">
    <input type="text" name="fullname" />
    <input type="email" name="email" />

    <%
    Dim uploader
    Set uploader = new AspUploader
    uploader.Name = "photo"
    uploader.MaxSizeKB = 2048
    uploader.AllowedFileExtensions = "*.jpg,*.png"
    uploader.InsertText = "Choose a profile picture"
    %>
    <%= uploader.GetString() %>

    <input type="submit" value="Register" />
</form>

Note there is no enctype and no <input type="file">. On postback:

<%
Dim name, email, mvcfile, list

name  = Request.Form("fullname")          ' works
email = Request.Form("email")             ' works

If Request.Form("photo") & "" <> "" Then
    list = Split(Request.Form("photo"), "/")
    Set mvcfile = uploader.GetUploadedFile(list(0))
    mvcfile.MoveTo Server.MapPath("avatars/" & list(0) & ".jpg")
End If
%>

There is a secondary benefit that is easy to overlook: by the time the user finishes typing, the photo has already finished uploading, so submitting the form is instant instead of a two-minute wait.

Fix 2: Parse the multipart body yourself

If you must keep a classic multipart post, you read the raw body and pull out both the fields and the file. In outline:

<%
Dim binData, boundary

binData  = Request.BinaryRead(Request.TotalBytes)
boundary = Request.ServerVariables("HTTP_CONTENT_TYPE")
boundary = Mid(boundary, InStr(boundary, "boundary=") + 9)

' For each part between boundaries:
'   read the Content-Disposition header
'   if it has a filename= -> it is the file, write the bytes with ADODB.Stream
'   otherwise             -> it is a text field, decode it and stash it in a Dictionary
%>

It works, and it costs you the whole request in memory plus every binary-safe string bug VBScript has to offer. Classic ASP upload methods compared covers when this is and is not a reasonable trade.

Fix 3: Two forms, or fields in the query string

Occasionally the simplest thing is to not mix them at all: submit the text fields to one page, and have the upload handler receive its context in the query string.

uploader.UploadUrl = "save-photo.asp?userid=" & CLng(Session("UserID"))

Note CLng(). Anything you concatenate into a URL or a SQL statement needs the same treatment — and never put personal data in a query string, since it lands in the IIS logs.

Patterns that come up next

Validation errors that must not lose the file

If the form redisplays with "email already taken", the uploaded file should still be attached. Carry the file's GUID in a hidden field across postbacks:

<%
Dim processedlist
processedlist = Request.Form("processedlist") & ""

If Request.Form("photo") & "" <> "" Then
    If processedlist = "" Then
        processedlist = Request.Form("photo")
    Else
        processedlist = processedlist & "/" & Request.Form("photo")
    End If
End If
%>
<input type="hidden" name="processedlist" value="<%= processedlist %>" />

There is a live demo of this.

Do not submit while the upload is running

Disable the submit button until the queue is finished, or a user who clicks fast will post a form referencing a file that is not there yet:

<script type="text/javascript">
function CuteWebUI_AjaxUploader_OnPostback() {
    document.getElementById("submitbutton").disabled = false;
}
</script>

Frequently asked questions

Why is Request.Form empty when my form has a file input?

Because the file input forces multipart/form-data, and Classic ASP only parses application/x-www-form-urlencoded. Every field vanishes from Request.Form, not just the file.

Can I upload a file and submit text fields in the same Classic ASP form?

Yes. Either parse the multipart body yourself, or let the file upload on its own background request so the form stays urlencoded and Request.Form keeps working.

Why do I get ASP 0104 when reading the request?

You called Request.BinaryRead after Request.Form had already consumed the body — or the body exceeded maxRequestEntityAllowed.

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.

Download the free trial Try the live demo Pricing