Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at the New York ColdFusion User Group (Jan. 2010) with: Clark Valberg and Javier Julio
Ben Nadel at the New York ColdFusion User Group (Jan. 2010) with: Clark Valberg@clarkvalberg ) and Javier Julio

Uploading And Sending MMS (Multimedia Messaging Service) Messages With Twilio And ColdFusion

By Ben Nadel on
Tags: 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:


 
 
 

 
 Sening MMS messages using Twilio and ColdFusion. 
 
 
 

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.




Reader Comments