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

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.

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

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>
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 ship in the core runtime.

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