This CFC wrapper will easily allow developers to integrate the upload functionality into any projects, and will enable users to upload an image to the TwitPic site with an optional message.
The full open source code can be downloaded from http://twitpic.riaforge.org/.
Instantiating the CFC
Invoking the twitpic object is incredibly simple.
The component has two required parameters; the account name and password for the Twitter user account.
The object also accepts an optional third parameter, 'parseResults'. This is set to false by default, and as such the XML response returned from the API will be in literal String format.
Setting this param to 'true', the XML will be returned through ColdFusion's built-in XmlParse() function, returning a structural representation of the XML.
<cfscript> strUsername = 'Twitter account username'; strPassword = 'Twitter account password'; // instantiate the twitpic component and pass the params objTwitPic = createObject('component', 'com.coldfumonkeh.twitpic').init( userName=strUsername, password=strPassword, parseResults='true'); </cfscript>
In my (some may say crude) HTML form, we are creating a very simple user-interface. We are allowing the user to browse for a file, and supply an optional message to send through to the TwitPic API. If the message was present, it would be posted using the Twitter user's details onto the feed with a link to the uploaded image prepended.
Handling the form upload
<cfif structKeyExists(form, 'submit_btn')> <cffile action="upload" destination="#getTempDirectory()#" file="#form.file#" nameconflict="overwrite" /> <cfscript> upload = objTwitPic.uploadPic( media='#cffile.serverdirectory#\#cffile.serverfile#', message=form.message); writeDump(upload); </cfscript> </cfif> <cfoutput> <form name="upload" action="#cgi.script_name#" method="post" enctype="multipart/form-data"> <label for="file">Photo:</label> <input type="file" name="file" id="file" /><br /> <label for="message">Tweet:</label> <input type="text" name="message" id="message" maxlength="140" size="70" /><br /> <input type="submit" name="submit_btn" value="upload" /> </form> </cfoutput>
In the above example. the form handler event itself is relatively easy. Detecting the submission of the form, the file is uploaded to the temporary directory on the ColdFusion server using the cffile tag.
To send the details to TwitPic, we next run the uploadPic() method within the twitpic.cfc, which accepts the location of the file and the message field from the form.
The uploadPic() Method
The uploadPic() method itself submits an HTTP POST request to the TwitPic API, sending through the file and the message field (if filled in).
<cffunction name="uploadPic" access="public" output="false" hint="I am the function that handles the file upload, and can also publish a status message to your Twitter account"> <cfargument name="media" required="true" type="string" hint="The binary image data to submit" /> <cfargument name="username" required="false" default="#getTweetUserName()#" type="string" hint="The Twitter username" /> <cfargument name="password" required="false" default="#getTweetPassword()#" type="string" hint="The Twitter password" /> <cfargument name="message" required="false" default="" type="string" hint="Message to post to Twitter. The URL of the image is automatically added" /> <cfset var cfhttp = '' /> <cfset var strResponse = '' /> <cfset var strMethodURL = '' /> <cfset var stuError = structNew() /> <cfset var arrXMLError = arrayNew(1) /> <!--- build the required URL for submission ---> <cfset strMethodURL = getTwitPicURL() & 'api/upload' /> <cfif len(arguments.message)> <cfset strMethodURL = strMethodURL & 'AndPost' /> </cfif> <!--- send the POST request to the URL ---> <cfhttp url="#strMethodURL#" method="post"> <cfhttpparam name="media" type="file" file="#arguments.media#" /> <cfhttpparam name="username" type="formfield" value="#arguments.username#" /> <cfhttpparam name="password" type="formfield" value="#arguments.password#" /> <cfif len(arguments.message)> <cfhttpparam name="message" type="formfield" value="#arguments.message#" /> </cfif> </cfhttp> <!--- handle the return ---> <cfif len(cfhttp.fileContent)> <cfset arrXMLError = xmlSearch(cfhttp.fileContent, 'rsp/err') /> <cfif arrayLen(arrXMLError)> <cfset stuError = arrXMLError.XmlAttributes /> <cfreturn stuError /> <cfabort> </cfif> <cfreturn handleReturnFormat(cfhttp.fileContent) /> </cfif> </cffunction>
The API offered two options when sending data; with a message or without the message.
The actual underlying code and functionality between the two was the same, the only difference being the URL to submit to if the message was supplied.
As such, it made it much simpler to create a single method to handle both eventualites, and generate the required URL accordingly, as seen in the code example below:
<!--- build the required URL for submission ---> <cfset strMethodURL = getTwitPicURL() & 'api/upload' /> <cfif len(arguments.message)> <cfset strMethodURL = strMethodURL & 'AndPost' /> </cfif>
If only the photo was supplied, the generated string would read:
With the inclusion of the message, the strMethodURL variable would be amended to create the revised URL:
Following a successful post, the API will return an XML response. For any posts that included only photo uploads (no comments or messages), the standard XML output is similar to below, returning the specific ID of the media and the direct link to the file on the twitpic site.
Supplying a message to accompany the uploaded file, the returned output would be similar to the screen grab below:
Here, you can see the response has included the Twitter ID of the user making the post, and the ID of the status created and published in the Twitter feed.
The freshly uploaded image and accompanying message / status update can now be viewed on TwitPic using the URL returned from the response.
The TwitPic API is incredibly simple, especially in comparison to other APIs, including the Twitter API. No complex REST-ful coding here. The output is available in XML only.
To some extent, that may be limiting. As a developer, ideally you would like an API to return method responses in as many varying formats as possible to help with code integration and development.
In this instance, however, it makes it incredbily easy to manage any errors from the API call.
The API itself only specifies four error messages, or instances for error based upon API process, namely
- 1001 - Invalid twitter username or password
- 1002 - Image not found
- 1003 - Invalid image type
- 1004 - Image larger than 4MB
The detection of error messages was made incredibly easy due to the fact that the XML response would contain a specific node called 'err', as shown in the below sample:
To check for the existence of this node, and therefore any error messages returned from the API, an XmlSearch is performed on the returned XML structure, performing a search on the 'err' node name:
<cfset arrXMLError = xmlSearch(cfhttp.fileContent, 'rsp/err') />
Any detection of the 'err' node will populate a pre-defined var-scoped array variable. From this, we can then run the checks on the length of the array, and return the structure within the attributes of the XML to the user, should an error exist.
<cfif arrayLen(arrXMLError)> <cfset stuError = arrXMLError.XmlAttributes /> <cfreturn stuError /> <cfabort> </cfif>
Where can I get it?
The code is available to download from RIAforge.org, here: http://twitpic.riaforge.org/