Uploading And Sending MMS (Multimedia Messaging Service) Messages With Twilio And ColdFusion
Last year, I blogged about sending MMS (Multimedia Messaging Service) messages using the Twilio API. However, at the time, Twilio only supported MMS messages on Canadian phone numbers and US short-codes (ie, those 5-digit phone numbers). Yesterday, however, Twilio finally announced full MMS support on all US phone numbers. So, naturally, I had to make sure my previous blog post worked.
My first blog post on Twilio and MMS messages was written about a year ago, so I wasn't sure if it was still relevant. Much to my supreme happiness, however, the demo worked perfectly, sending out an MMS message, without having to change a single line of code. Now, I could have just said, "Hey, this just works." But, what would be the fun in that? We're programmers, dangit - we have to "build" stuff. So, instead, I wanted to re-tool the previous example to use an upload form.
In the following code, I'm taking my prior demo and working it into an upload form where the user can select a target phone number, enter a message, and upload a photo. For the sake of simplicity, I'm hard-coding the requirement for JPEG images. This way, I don't have to worry about checking mime-types.
When the user selects the photo, I first upload it to my server. But, since I'm actually on a local development environment, the upload can't be publicly available. As such, I then need to push the upload to Amazon S3 where it can be accessed by the Twilio servers.
<cfscript> | |
// Include the credentials for both Amazon S3 as well as Twilio. | |
include "./credentials.cfm"; | |
// ------------------------------------------------------ // | |
// ------------------------------------------------------ // | |
// Param the form inputs. | |
param name="form.submitted" type="boolean" default="false"; | |
param name="form.phone" type="string" default=""; | |
param name="form.message" type="string" default=""; | |
param name="form.upload" type="string" default=""; | |
errorMessage = ""; | |
// Check to see if the user has submitted the form. | |
if ( form.submitted ) { | |
try { | |
// Sanitize the phone number. | |
form.phone = reReplace( form.phone, "[^\d]+", "", "all" ); | |
// Validate phone number. | |
if ( len( form.phone ) neq 10 ) { | |
throw( | |
type = "InvalidField", | |
message = "Please provide a valid 10 digit phone number." | |
); | |
} | |
// Validate message. | |
if ( ! len( form.message ) ) { | |
throw( | |
type = "InvalidField", | |
message = "Don't be rude, say something." | |
); | |
} | |
// Validate file selection (for existence only). | |
if ( ! len( form.upload ) ) { | |
throw( | |
type = "InvalidField", | |
message = "Please select a photo to upload." | |
); | |
} | |
// If we made it this far, the form data is valid, as best we can determine | |
// before processing the upload. | |
// Upload the file to our server. | |
// -- | |
// NOTE: For this demo, we're going to hard-code the requirement of the | |
// uploaded asset to be a JPEG image. | |
upload = fileUpload( | |
expandPath( "./uploads" ), | |
"upload", | |
"image/jpeg", | |
"makeUnique" | |
); | |
// Twilio can't handle direct file uploads (as far as I know), so we have to | |
// provide a "media URL". This means that our MMS media has to be publicly | |
// accessible for some period of time. To accomplish this, we'll upload it | |
// Amazon S3 as a public-read object for the Twilio interaction. | |
// -- Step 1: Push Asset to Amazon S3. -- // | |
// Read in the content of the uploaded file. | |
media = fileReadBinary( expandPath( "./uploads/#upload.serverFile#" ) ); | |
// Define the Amazon S3 resource. | |
// -- | |
// NOTE: This demo does not take into account url-encoding special characters. | |
// I'm just keeping it simple for the demo (since I know nothing will break). | |
resource = "/#aws.bucket#/twilio/#upload.serverFile#"; | |
// S3 has a limited window for when a request is valid. We need to tell it | |
// when this request was prepared. | |
currentTime = getHttpTimeString( now() ); | |
// The mime-type will be stored as Meta-data on the S3 resource; Amazon will | |
// also provide this as the Content-Type header when serving the file. | |
// -- | |
// NOTE: I'm hard-coding the mime-type for the demo. | |
contentType = "image/jpeg"; | |
// Set up the S3 signature assets. | |
// -- | |
// NOTE: I am setting the Amazon S3 resource as "PUBLIC-READ"; this way, we | |
// do NOT have to generate a pre-signed URL for Twilio - we can provide a URL | |
// directly to this resource. | |
stringToSignParts = [ | |
"PUT", | |
"", | |
contentType, | |
currentTime, | |
"x-amz-acl:public-read", | |
resource | |
]; | |
stringToSign = arrayToList( stringToSignParts, chr( 10 ) ); | |
// Generate the Hmac-SHA1 of the signature. | |
signature = new Crypto().hmacSha1( | |
aws.secretKey, | |
stringToSign, | |
"base64" | |
); | |
// Upload the image asset to Amazon S3. | |
s3Request = new Http( | |
method = "put", | |
url = "https://s3.amazonaws.com#resource#" | |
); | |
s3Request.addParam( | |
type = "header", | |
name = "Authorization", | |
value = "AWS #aws.accessID#:#signature#" | |
); | |
s3Request.addParam( | |
type = "header", | |
name = "Content-Length", | |
value = arrayLen( media ) | |
); | |
s3Request.addParam( | |
type = "header", | |
name = "Content-Type", | |
value = contentType | |
); | |
s3Request.addParam( | |
type = "header", | |
name = "Date", | |
value = currentTime | |
); | |
s3Request.addParam( | |
type = "header", | |
name = "x-amz-acl", | |
value = "public-read" | |
); | |
s3Request.addParam( | |
type = "body", | |
value = media | |
); | |
result = s3Request.send(); | |
// Make sure the upload to Amazon S3 was successful. | |
if ( ! reFind( "2\d\d", result.getPrefix().statusCode ) ) { | |
throw( | |
type = "S3", | |
message = "The Amazon S3 upload API request failed.", | |
extendedInfo = duplicate( result.getPrefix() ) | |
); | |
} | |
// ------------------------------------------------------ // | |
// ------------------------------------------------------ // | |
// -- Step 2: Send The MMS Message. -- // | |
// Now that we've uploaded the asset to Amazon S3 with *PUBLIC* access, we | |
// can send the MMS message via Twilio. | |
// Get the full Amazon S3 resource url using the resource from above. | |
mediaUrl = "https://s3.amazonaws.com#resource#"; | |
// Send the MMS message via Twilio's REST API. | |
twilioRequest = new Http( | |
method = "post", | |
url = "https://api.twilio.com/2010-04-01/Accounts/#twilio.accountSID#/Messages", | |
username = twilio.accountSID, | |
password = twilio.authToken | |
); | |
twilioRequest.addParam( | |
type = "formfield", | |
name = "From", | |
value = twilio.phone | |
); | |
twilioRequest.addParam( | |
type = "formfield", | |
name = "To", | |
value = "+1#form.phone#" | |
); | |
twilioRequest.addParam( | |
type = "formfield", | |
name = "MediaUrl", | |
value = mediaUrl | |
); | |
twilioRequest.addParam( | |
type = "formfield", | |
name = "Body", | |
value = form.message | |
); | |
result = twilioRequest.send(); | |
// Make sure that the Twilio request was initiated. | |
if ( ! reFind( "2\d\d", result.getPrefix().statusCode ) ) { | |
throw( | |
type = "Twilio", | |
message = "The Twilio MMS API request failed.", | |
extendedInfo = duplicate( result.getPrefix() ) | |
); | |
} | |
// If we made it this far, everything worked like a champ! The file was | |
// uploaded to Amazon S3, then the public URL for said object was used to | |
// send the Twilio MMS message. Send user to the confirmation page for much | |
// deserved celebration and high-fiving! | |
location( url = "./confirmation.cfm", addToken = false ); | |
// Catch any form-validation error. | |
} catch ( InvalidField error ) { | |
errorMessage = error.message; | |
// Catch any unexpected error. | |
} catch ( any error ) { | |
errorMessage = "For some reason, we couldn't send your MMS message."; | |
} | |
} // END: if submitted. | |
</cfscript> | |
<cfoutput> | |
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title> | |
Sending MMS (Multimedia Messaging Service) Messages With Twilio And ColdFusion | |
</title> | |
</head> | |
<body> | |
<h1> | |
Sending MMS (Multimedia Messaging Service) Messages With Twilio And ColdFusion | |
</h1> | |
<!--- If we have an error message, show it. ---> | |
<cfif len( errorMessage )> | |
<p> | |
<strong>Oops!</strong> #htmlEditFormat( errorMessage )# | |
</p> | |
</cfif> | |
<form | |
method="post" | |
action="#cgi.script_name#" | |
enctype="multipart/form-data"> | |
<input type="hidden" name="submitted" value="true" /> | |
<p> | |
Phone Number:<br /> | |
<input type="text" name="phone" size="20" /> | |
</p> | |
<p> | |
Message:<br /> | |
<input type="text" name="message" size="40" /> | |
</p> | |
<p> | |
Photo:<br /> | |
<input type="file" name="upload" size="20" /> | |
</p> | |
<p> | |
<input type="submit" value="Send MMS Message" /> | |
</p> | |
</form> | |
</body> | |
</html> | |
</cfoutput> |
There's not a whole lot going on here - so I won't go into any further detail. Most of it is just the noise of generating and signing HTTP requests. But, when I upload a photo, I am able to get the MMS message on my iPhone:

Woot! And double-woot! Can you think of a better way to start your Friday off than by getting MMS-enabled?
Twilio's approach to telephony revolutionized the way we developers can think about our application interactions. And, now, they've freakin' done it again. Twilio is definitely one of my favorite 3rd-party services and a goto integration point for just about every application I build.
Want to use code from this post? Check out the license.
Reader Comments
It is very useful for me. Already I have used twilio service with out upload picture. Only sending a message.
Nice and thanks Ben
@Dhuvarakaikannan,
Thanks! Glad you found this helpful :D