Multimedia Content Integration
HTML5 media elements
HTML5 ships two native media elements — <video> and <audio> — that the browser renders with built-in playback controls. No plugins, no Flash, no third-party players required.
ColdFusion's role is server-side: it stores metadata, serves file paths, and handles uploads. The browser's <video> and <audio> elements do the actual playback.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Media Demo</title>
</head>
<body>
<h2>Video</h2>
<video controls width="640" preload="metadata">
<source src="/media/sample.mp4" type="video/mp4">
<source src="/media/sample.webm" type="video/webm">
Your browser does not support HTML5 video.
</video>
<h2>Audio</h2>
<audio controls preload="metadata">
<source src="/media/sample.mp3" type="audio/mpeg">
<source src="/media/sample.ogg" type="audio/ogg">
Your browser does not support HTML5 audio.
</audio>
</body>
</html>
Multiple <source> tags let the browser pick the first format it supports — MP4/MP3 for broad compatibility, WebM/OGG as open-format fallbacks.
Video and audio format compatibility — which to use?
| Format | MIME type | Browser support | Notes |
|---|---|---|---|
| MP4 (H.264) | video/mp4 | All modern browsers | Best compatibility — use as primary |
| WebM (VP9) | video/webm | Chrome, Firefox, Edge | Open format, smaller file size |
| OGG Theora | video/ogg | Firefox, Chrome | Older open format, less common today |
| MP3 | audio/mpeg | All modern browsers | Best audio compatibility |
| OGG Vorbis | audio/ogg | Firefox, Chrome | Open format audio fallback |
The practical rule: always provide MP4/MP3 first. Add WebM/OGG as a second <source> for open-format coverage. The browser picks the first it can play.

cffile action="upload" handles the entire multipart pipeline — parsing, validation, name-conflict resolution, and disk write.
Activity 1 — Create a media demo page
Activity: In the Terminal tab, create media_demo.cfm — an HTML5 page with both a <video> and an <audio> element. Because the lab does not have real media files, both elements use placeholder src paths but still demonstrate the correct markup and ColdFusion dynamic output:
sudo tee /opt/coldfusion2025/cfusion/wwwroot/media_demo.cfm << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ColdFusion Media Demo</title>
<style>
body { font-family: sans-serif; max-width: 700px; margin: 2rem auto; }
video, audio { display: block; margin: 1rem 0; }
</style>
</head>
<body>
<h1>ColdFusion Media Demo</h1>
<cfoutput>
<p>Page generated at: <strong>#timeFormat(now(), "HH:mm:ss")#</strong></p>
</cfoutput>
<h2>Video</h2>
<video controls width="640" preload="metadata">
<source src="/media/sample.mp4" type="video/mp4">
<source src="/media/sample.webm" type="video/webm">
<p>Your browser does not support HTML5 video.</p>
</video>
<h2>Audio</h2>
<audio controls preload="metadata">
<source src="/media/sample.mp3" type="audio/mpeg">
<source src="/media/sample.ogg" type="audio/ogg">
<p>Your browser does not support HTML5 audio.</p>
</audio>
</body>
</html>
EOF
Verify the page is served:
curl -s http://localhost:8500/media_demo.cfm | head -20

media_demo.cfm with both HTML5 <video> and <audio> elements and a CFML-rendered timestamp.
HTML5 video and audio elements in depth
The controls attribute renders the browser's native playback UI. Additional attributes let you fine-tune behaviour:
| Attribute | Effect |
|---|---|
controls | Show play/pause, volume, seek bar |
autoplay | Start playing immediately (muted required in most browsers) |
muted | Start muted — required for autoplay in Chrome/Safari |
loop | Repeat indefinitely |
preload="none" | Don't load any data until the user presses play |
preload="metadata" | Load duration and dimensions only (default) |
preload="auto" | Load the whole file on page load |
poster="/img/thumb.jpg" | Image shown before playback starts (video only) |
width / height | Video display dimensions in pixels |
File upload with cffile
cffile action="upload" handles multipart form submissions. ColdFusion validates the MIME type, resolves filename conflicts, and writes the file to disk:
<cfscript>
if (structKeyExists(form, "mediaFile")) {
allowedTypes = "video/mp4,video/webm,audio/mpeg,audio/ogg";
cffile(
action = "upload",
filefield = "mediaFile",
destination = expandPath("/uploads/media/"),
accept = allowedTypes,
nameconflict = "makeunique"
);
writeOutput("Uploaded: " & cffile.serverFile);
}
</cfscript>
<form method="post" enctype="multipart/form-data">
<input type="file" name="mediaFile" accept="video/*,audio/*">
<button type="submit">Upload</button>
</form>
Key cffile properties after upload:
| Property | Value |
|---|---|
cffile.serverFile | Filename on disk (after conflict resolution) |
cffile.serverDirectory | Destination directory |
cffile.fileSize | Size in bytes |
cffile.contentType | MIME type reported by the browser |
Security rules for file uploads
Client-submitted data — including file names and MIME types — is untrusted. A malicious user can rename a .php file to video.mp4 and submit it. Always apply all three defences:
- Validate the MIME type server-side — the
acceptattribute oncffilechecks theContent-Typeheader, but also inspect the actual file bytes withimageIsValid()for images or a magic-bytes check for video. - Whitelist extensions explicitly:
allowedExts = ["mp4", "webm", "mp3", "ogg"];
ext = lCase(listLast(cffile.serverFile, "."));
if (!arrayFind(allowedExts, ext)) {
fileDelete(cffile.serverDirectory & cffile.serverFile);
throw(message="Disallowed file type.");
}
- Store uploads outside the web root when files should not be directly browsable. Serve them through a CF endpoint that checks permissions first.
Activity 2 — Create a file upload handler
Activity: Create upload_media.cfm — a page with a cffile upload handler and a form that accepts video and audio files:
sudo mkdir -p /opt/coldfusion2025/cfusion/wwwroot/uploads/media
sudo tee /opt/coldfusion2025/cfusion/wwwroot/upload_media.cfm << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Media</title>
<style>
body { font-family: sans-serif; max-width: 520px; margin: 2rem auto; }
.result { margin-top: 1rem; padding: 1rem; background: #f0f4ff; border-left: 4px solid #3b82d4; }
.error { color: #c0392b; }
</style>
</head>
<body>
<h1>Upload Media File</h1>
<cfif cgi.REQUEST_METHOD eq "POST" and structKeyExists(form, "mediaFile") and len(form.mediaFile)>
<cfscript>
allowedTypes = "video/mp4,video/webm,audio/mpeg,audio/ogg";
allowedExts = ["mp4", "webm", "mp3", "ogg"];
uploadDir = expandPath("/uploads/media/");
try {
cffile(
action = "upload",
filefield = "mediaFile",
destination = uploadDir,
accept = allowedTypes,
nameconflict = "makeunique"
);
ext = lCase(listLast(cffile.serverFile, "."));
if (!arrayFind(allowedExts, ext)) {
fileDelete(cffile.serverDirectory & "/" & cffile.serverFile);
throw(message="Disallowed file extension: #ext#");
}
} catch (any e) {
uploadError = e.message;
}
</cfscript>
<cfif isDefined("uploadError")>
<div class="result"><span class="error">Upload failed: <cfoutput>#encodeForHTML(uploadError)#</cfoutput></span></div>
<cfelse>
<div class="result">
<strong>Upload successful!</strong><br>
<cfoutput>
File: <strong>#encodeForHTML(cffile.serverFile)#</strong><br>
Size: <strong>#cffile.fileSize# bytes</strong><br>
Type: <strong>#encodeForHTML(cffile.contentType)#</strong>
</cfoutput>
</div>
</cfif>
</cfif>
<form method="post" enctype="multipart/form-data">
<label for="mediaFile">Choose a video or audio file:</label><br><br>
<input type="file" id="mediaFile" name="mediaFile" accept="video/*,audio/*" required>
<br><br>
<button type="submit">Upload</button>
</form>
</body>
</html>
EOF
Verify the upload handler is accessible:
curl -s -o /dev/null -w "%{http_code}" http://localhost:8500/upload_media.cfm
You should see 200.

upload_media.cfm — the upload form before and after a successful file submission, with ColdFusion echoing back the server filename, size, and MIME type.
Image manipulation with cfimage
ColdFusion ships with a built-in image manipulation library — no external dependencies needed:
<cfscript>
// Resize an uploaded image to a 200×200 thumbnail
cfimage(
action = "resize",
source = "/uploads/original.jpg",
destination = "/uploads/thumb.jpg",
width = "200",
height = "200",
overwrite = true
);
</cfscript>

cfimage actions reference — resize, rotate, convert, addBorder, watermark, and captcha all ship in the core runtime.
All cfimage actions:
| Action | What it does |
|---|---|
resize | Scale image to new dimensions |
rotate | Rotate by degrees |
convert | Change format (e.g. JPG → PNG) |
addBorder | Add a coloured border |
watermark | Overlay semi-transparent text or image |
captcha | Generate a CAPTCHA challenge image |
read | Load image into a CF image object for scripted manipulation |
write | Save a CF image object to disk |
getInfo | Return width, height, format, and EXIF metadata |
Activity 3 — Generate a thumbnail with cfimage
Activity: Create image_thumb.cfm — a page that uses cfimage to generate a thumbnail from a small test image and display both the original and the resized result:
# Create a minimal test image using ColdFusion itself
sudo tee /opt/coldfusion2025/cfusion/wwwroot/image_thumb.cfm << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>cfimage Thumbnail Demo</title>
<style>
body { font-family: sans-serif; max-width: 700px; margin: 2rem auto; }
.images { display: flex; gap: 2rem; align-items: flex-start; margin-top: 1rem; }
figure { margin: 0; text-align: center; }
figcaption { font-size: .85rem; color: #555; margin-top: .4rem; }
</style>
</head>
<body>
<h1>cfimage — Thumbnail Demo</h1>
<cfscript>
srcPath = expandPath("/uploads/test_original.jpg");
thumbPath = expandPath("/uploads/test_thumb.jpg");
// Generate a solid-colour test image if it doesn't exist yet
if (!fileExists(srcPath)) {
img = imageNew("", 400, 300, "rgb", "##4a90d9");
imageSetDrawingColor(img, "white");
imageDrawText(img, "ColdFusion Test Image 400x300", 60, 145);
imageWrite(img, srcPath);
}
// Resize to 200×150 thumbnail
cfimage(
action = "resize",
source = srcPath,
destination = thumbPath,
width = "200",
height = "150",
overwrite = true
);
origImg = imageRead(srcPath);
thumbImg = imageRead(thumbPath);
origInfo = imageInfo(origImg);
thumbInfo = imageInfo(thumbImg);
</cfscript>
<div class="images">
<figure>
<img src="/uploads/test_original.jpg" width="400" height="300" alt="Original">
<figcaption>
<cfoutput>Original — #origInfo.width#×#origInfo.height# px</cfoutput>
</figcaption>
</figure>
<figure>
<img src="/uploads/test_thumb.jpg" width="200" height="150" alt="Thumbnail">
<figcaption>
<cfoutput>Thumbnail — #thumbInfo.width#×#thumbInfo.height# px</cfoutput>
</figcaption>
</figure>
</div>
</body>
</html>
EOF
Open /image_thumb.cfm in the ColdFusion 2025 browser tab. ColdFusion will generate the test image programmatically and display both original and thumbnail side by side.
curl -s -o /dev/null -w "%{http_code}" http://localhost:8500/image_thumb.cfm
cfimage resizing a programmatically generated test image — original at 400×300 and thumbnail at 200×150, dimensions confirmed by imageInfo().
Troubleshooting — "Variable IMAGEGETINFO is undefined"
If you see this error when loading image_thumb.cfm:

ColdFusion 2025 error — imageGetInfo() does not exist as a standalone function.
What it means: an earlier version of this lesson used imageGetInfo(), which does not exist in ColdFusion 2025. The correct function is imageInfo().
The fix — use imageInfo() on an image object returned by imageRead():
// ✗ Wrong — imageGetInfo() is not a CF2025 function
origInfo = imageGetInfo(imageRead(srcPath));
// ✓ Correct
origImg = imageRead(srcPath);
origInfo = imageInfo(origImg);
writeOutput(origInfo.width & "×" & origInfo.height);
The current code in this lesson already uses imageInfo(). If you see this error it means you are running an older copy of image_thumb.cfm — re-run the sudo tee command above to replace it with the corrected version.
When all the checks above are green, this lesson is complete. Your progress is saved automatically.
🎉 Congratulations — you have reached the end of Unit 1!
You have covered a lot of ground: CFML syntax, variables and scopes, the application lifecycle, object-oriented programming with CFCs, HTML5 integration, and multimedia handling. That is a solid foundation.
To reinforce everything you have learned, your next step is the Unit 1 Challenge. The challenge brings together concepts from across all seven lessons into a single hands-on task. Feel free to review any lesson, re-read the hint boxes, or consult external sources — that is not cheating, that is how real developers work.
Take your time, trust the process, and keep up the hard work. You've got this. 💪
- Previous lesson
- HTML5 and Advanced ColdFusion Features
- Next lesson
- Datasource Configuration