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