Building an Image Upload and Gallery Page in Classic ASP
From the upload button to a working gallery, including the checks that keep it from becoming a shell.
Step 1: The upload control
Image galleries have a narrower set of requirements than general file upload, and you should encode all of them up front — type, size, and count:
<%@ 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 = 8192 ' 8 MB per photo
uploader.MaxFilesLimit = 30
uploader.AllowedFileExtensions = "*.jpg,*.jpeg,*.png,*.gif,*.webp"
uploader.InsertText = "Add photos"
uploader.FileTypeNotSupportMsg = "Images only, please - JPG, PNG, GIF or WEBP."
uploader.FileTooLargeMsg = "That image is over the 8 MB limit."
%>
<%= uploader.GetString() %>
<input type="submit" value="Add to gallery" />
</form>
Because those limits are enforced in the browser, someone picking a 400 MB video finds out immediately rather than after a long upload.
Step 2: Prove the files really are images
An extension is a claim, and Content-Type is a claim the client makes about its own file.
Neither is evidence. Read the first bytes:
<%
Function ImageKind(path)
Dim stm, head, i, hex
ImageKind = ""
Set stm = Server.CreateObject("ADODB.Stream")
stm.Type = 1
stm.Open
stm.LoadFromFile path
head = stm.Read(12)
stm.Close
hex = ""
For i = 1 To LenB(head)
hex = hex & Right("0" & Hex(AscB(MidB(head, i, 1))), 2)
Next
If Left(hex, 6) = "FFD8FF" Then ImageKind = "jpg"
If Left(hex, 16) = "89504E470D0A1A0A" Then ImageKind = "png"
If Left(hex, 8) = "47494638" Then ImageKind = "gif"
If Mid(hex, 1, 8) = "52494646" And Mid(hex, 17, 8) = "57454250" Then ImageKind = "webp"
End Function
%>
A file can be a perfectly valid JPEG and carry ASP source appended to it. Magic-number checks are a second layer — the first is making sure the folder images land in cannot execute anything. Details in the upload security checklist.
Step 3: Store with a generated name
Never use the visitor's file name as a path. Two people will upload IMG_0042.jpg on the same
afternoon, and one of them will be trying something cleverer than that:
<%
Dim fso, destDir, list, i, mvcfile, kind, storedName
Set fso = Server.CreateObject("Scripting.FileSystemObject")
destDir = Server.MapPath("gallery")
If Not fso.FolderExists(destDir) Then fso.CreateFolder destDir
If Request.Form("myuploader") & "" <> "" Then
list = Split(Request.Form("myuploader"), "/")
For i = 0 To UBound(list)
Set mvcfile = uploader.GetUploadedFile(list(i))
kind = ImageKind(mvcfile.GetTempFilePath())
If kind = "" Then
Response.Write "Rejected (not an image): " & Server.HTMLEncode(mvcfile.FileName) & "<br/>"
mvcfile.Delete
Else
' the GUID is already unique - reuse it, with the extension we verified
storedName = list(i) & "." & kind
mvcfile.MoveTo destDir & "\" & storedName
SavePhotoRow storedName, mvcfile.FileName, mvcfile.FileSize, Session("UserID")
End If
Next
End If
%>
The original name is still worth keeping — as a caption, as display text, as something to search on — just never as a path. Store it in the database and HTML-encode it on the way out.
Step 4: Render the gallery
<%
Dim rs
Set rs = cn.Execute("SELECT PhotoId, StoredName, OriginalName " & _
"FROM Photos WHERE OwnerId = " & CLng(Session("UserID")) & _
" ORDER BY UploadedUtc DESC")
%>
<div class="gallery">
<%
Do While Not rs.EOF
%>
<figure>
<img src="gallery/<%= rs("StoredName") %>"
alt="<%= Server.HTMLEncode(rs("OriginalName")) %>"
loading="lazy" width="240" height="180" />
<figcaption><%= Server.HTMLEncode(rs("OriginalName")) %></figcaption>
</figure>
<%
rs.MoveNext
Loop
%>
</div>
Three details worth keeping:
Server.HTMLEncodeon the original name. A file called<img src=x onerror=alert(1)>.jpgis a stored XSS payload otherwise.loading="lazy"plus explicitwidth/height— the browser skips off-screen images and the page stops jumping as they load.- Full-size images in an
<img>tag are the classic gallery performance mistake. Thumbnails matter, which brings us to the next step.
Step 5: Thumbnails
Classic ASP has no image processing of its own, so resizing needs help from outside the script. The realistic options:
| Approach | Needs | Notes |
|---|---|---|
| CSS/HTML sizing only | Nothing | Simplest, but the browser still downloads full-size images. Fine for a handful, not for a hundred |
| Resize in the browser before upload | Canvas API in JavaScript | Saves bandwidth too; the original never leaves the machine unless you also send it |
| An image COM component | Server admin rights | Reintroduces the registration problem a pure ASP stack avoids |
| A scheduled task or side service | A process outside IIS | Keeps the web tier simple; thumbnails appear a moment later |
For most galleries, resizing on the client before upload is the best trade — smaller uploads, faster pages, and no server dependency:
function shrink(file, maxEdge, done) {
var img = new Image();
img.onload = function () {
var scale = Math.min(1, maxEdge / Math.max(img.width, img.height));
var c = document.createElement("canvas");
c.width = Math.round(img.width * scale);
c.height = Math.round(img.height * scale);
c.getContext("2d").drawImage(img, 0, 0, c.width, c.height);
c.toBlob(done, "image/jpeg", 0.85);
};
img.src = URL.createObjectURL(file);
}
Gallery checklist
- Extension allow-list and magic-number verification.
- Execute permission removed from the gallery folder.
- Generated storage names; original names kept only as data.
Server.HTMLEncodeon every name you render.- Per-file size cap and a per-batch file count cap.
- Thumbnails, or at least lazy loading with explicit dimensions.
- Ownership recorded, so deletion can be authorised.
- Total-storage monitoring — galleries grow without anyone deciding they should.
Frequently asked questions
How do I allow only images to be uploaded in Classic ASP?
Set AllowedFileExtensions to the image types you accept, then verify the magic-number bytes
on the server. The extension alone is not evidence of anything.
Can Classic ASP resize an uploaded image?
Not on its own — it has no image processing. Resize in the browser with the canvas API before upload, or hand the work to a process outside IIS.
Where should uploaded images be stored?
On disk under generated names, with metadata in a database. Keep the folder non-executable, or store outside the webroot and serve through a script.
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.