Lesson  in  ColdFusion 2025: Foundations

Multimedia Content Integration

Embed and manage video, audio and other multimedia in ColdFusion applications. Use HTML5 media elements, manage uploads, and handle compatibility and performance considerations.

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?
FormatMIME typeBrowser supportNotes
MP4 (H.264)video/mp4All modern browsersBest compatibility — use as primary
WebM (VP9)video/webmChrome, Firefox, EdgeOpen format, smaller file size
OGG Theoravideo/oggFirefox, ChromeOlder open format, less common today
MP3audio/mpegAll modern browsersBest audio compatibility
OGG Vorbisaudio/oggFirefox, ChromeOpen 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.

Three-step upload flow diagram — step 1 "Browser" shows a multipart/form-data POST request with a file field highlighted; step 2 "ColdFusion cffile" shows the cffile tag parsing the upload, validating MIME type against the allowed list, and resolving name conflicts; step 3 "Disk" shows the final file written to /uploads/media/ with the serverFile, serverDirectory, and fileSize properties labelled on the output arrow

cffile action="upload" handles the entire multipart pipeline — parsing, validation, name-conflict resolution, and disk write.


Activity 1 — Create a media demo page

Activity: Create media_demo.cfm — an HTML5 page with <video> and <audio> elements and a CFML-rendered timestamp.

Terminal tab:

sudo tee /opt/coldfusion2025/cfusion/wwwroot/student/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
✏️ Using the IDE tab instead? Create the file here

In the IDE tab, click File → Open Folder…, type /opt/coldfusion2025/cfusion/wwwroot/student and press Enter. If VS Code asks "The folder does not exist. Would you like to create it?" — click Yes. Then right-click in the Explorer panel → New File → name it media_demo.cfm, paste the content below, and save with Ctrl+S:

<!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>

Verify the page is served:

curl -s http://localhost:8500/student/media_demo.cfm | head -20
Browser showing media_demo.cfm with the H1 heading "ColdFusion Media Demo", the server timestamp rendered by CFML, and two HTML5 media players — a video player and an audio player — each with native browser controls

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:

AttributeEffect
controlsShow play/pause, volume, seek bar
autoplayStart playing immediately (muted required in most browsers)
mutedStart muted — required for autoplay in Chrome/Safari
loopRepeat 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 / heightVideo 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:

PropertyValue
cffile.serverFileFilename on disk (after conflict resolution)
cffile.serverDirectoryDestination directory
cffile.fileSizeSize in bytes
cffile.contentTypeMIME 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:

  1. Validate the MIME type server-side — the accept attribute on cffile checks the Content-Type header, but also inspect the actual file bytes with imageIsValid() for images or a magic-bytes check for video.
  2. 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.");
}
  1. 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.

Terminal tab:

sudo mkdir -p /opt/coldfusion2025/cfusion/wwwroot/uploads/media

sudo tee /opt/coldfusion2025/cfusion/wwwroot/student/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
✏️ Using the IDE tab instead? Create the file here

In the IDE tab, first create the uploads directory from the Terminal tab:

sudo mkdir -p /opt/coldfusion2025/cfusion/wwwroot/uploads/media

Then in the IDE tab, open /opt/coldfusion2025/cfusion/wwwroot/student/, right-click in the Explorer panel → New File → name it upload_media.cfm, paste the content below, and save with Ctrl+S:

<!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>

Verify the upload handler is accessible:

curl -s -o /dev/null -w "%{http_code}" http://localhost:8500/student/upload_media.cfm

You should see 200.

Browser showing upload_media.cfm with the heading "Upload Media File", a file input field labelled "Choose a video or audio file", and an Upload button — and below it a blue confirmation box showing the uploaded filename, file size in bytes, and MIME type echoed back by ColdFusion

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

In ColdFusion 2025, image manipulation is provided by a separately installed package called image. In this lab environment it is pre-installed for you — cfimage, imageNew(), imageRead(), imageWrite(), and imageInfo() are all ready to use.

Why does the CF Admin Package Manager fail when trying to install "image"?

If you navigate to CF Admin → Package Manager and try to install the image package manually, you will see an error like:

Unable to connect to the Adobe Package Repository.
Connection timed out.

This is expected in the lab environment. The CF Package Manager tries to download packages from Adobe's servers at runtime — but the lab VM has no outbound internet access (this is a deliberate sandbox restriction).

The image package is already installed. It was downloaded and installed during the image build process, when internet access was available. You do not need to install it again.

The practical rule: in this lab, never try to install CF packages through the Admin UI. All packages needed for the curriculum are pre-installed at build time via cfpm.sh.

<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>
Grid of six labelled boxes showing cfimage actions — resize (thumbnail icon), rotate (circular arrow with degree label), convert (two file extension labels jpg↔png), addBorder (image with thick border), watermark (semi-transparent text overlaid on a photo), and captcha (distorted text challenge image) — each box has the action name in bold and a one-line description below

cfimage actions reference — resize, rotate, convert, addBorder, watermark, and captcha — all available via the pre-installed image package.

All cfimage actions:

ActionWhat it does
resizeScale image to new dimensions
rotateRotate by degrees
convertChange format (e.g. JPG → PNG)
addBorderAdd a coloured border
watermarkOverlay semi-transparent text or image
captchaGenerate a CAPTCHA challenge image
readLoad image into a CF image object for scripted manipulation
writeSave a CF image object to disk
getInfoReturn 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 and display both original and resized result.

Terminal tab:

# Create a minimal test image using ColdFusion itself
sudo tee /opt/coldfusion2025/cfusion/wwwroot/student/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
✏️ Using the IDE tab instead? Create the file here

In the IDE tab, right-click in the Explorer panel → New File → name it image_thumb.cfm, paste the content from the Terminal command above, and save with Ctrl+S.

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/student/image_thumb.cfm
Browser showing image_thumb.cfm with the heading "cfimage — Thumbnail Demo" and two images side by side — the original blue test image at 400×300 and the resized thumbnail at 200×150 — each with a figcaption showing the dimensions read back by ColdFusion imageInfo

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 error page showing "Variable IMAGEGETINFO is undefined" with the standard CF error layout

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. 💪