Using CFMAIL to send attachments stored in memory

Posted by Dan on Oct 26, 2007 @ 8:43 AM

Awhile back I was looking for a way to send e-mail attachments in CFMX without writing data to disk. That solution uses the Javamail API to directly send a e-mail with attachments to an SMTP server. This method also completely bypasses the CF mail spool.

Charlie Arehart linked to the article from an article he wrote about storing CFDOCUMENT and CFREPORT contents in a variable. In the comments on his post, Jon Wolski posted a solution that uses the built-in ColdFusion tags CFMAIL and CFMAILPART. So, I thought I'd re-do my original example using Jon's technique in this post—just so you can see an example of both methods.

Below is some source code that will show you how to use the CFMAIL tag to send a multipart message that contains:

  • Plain Text
  • HTML (with embedded images)
  • Two file attachments from binary data stored in memory
<!---// create a PDF document and save it to a variable //--->
<cfdocument format="pdf" name="binPdf">
    <h1>
        A PDF Document
    </h1>

    <p>
        This is a document rendered by the cfdocument tag
        at <cfoutput>#dateFormat(now(), "mmmm dd, yyyy")#
        #lCase(timeFormat(now(), "h:mmtt"))#</cfoutput>.
    </p>
    <table width="50%" border="2" cellspacing="2" cellpadding="2">
        <tr>
            <td><strong>Name</strong></td>
            <td><strong>Role</strong></td>
        </tr>
        <tr>
            <td>Bill</td>
            <td>Lead</td>
        </tr>
        <tr>
            <td>Susan</td>
            <td>Principal Writer</td>
        </tr>
        <tr>
            <td>Adelaide</td>
            <td>Part Time Senior Writer</td>
        </tr>
        <tr>
            <td>Thomas</td>
            <td>Full Time for 6 months</td>
        </tr>
        <tr>
            <td>Michael</td>
            <td>Full Time for 4 months</td>
        </tr>
    </table>
</cfdocument>

<!---// create a png chart and save it to a variable //--->
<cfchart name="binChart" format="png" font="arialunicodeMS" xaxistitle="Month" yaxistitle="Degrees Celsius" showlegend="yes">
    <cfchartseries type="line" serieslabel="Europe">
        <cfloop index="i" list="Apr,May,Jun,Jul,Aug,Sep">
            <cfchartdata item="#i#" value="#RandRange(12,42)#">
        </cfloop>
    </cfchartseries>
    <cfchartseries type="line" serieslabel="USA">
        <cfloop index="j" list="Apr,May,Jun,Jul,Aug,Sep">
            <cfchartdata item="#j#" value="#RandRange(12,42)#">
        </cfloop>
    </cfchartseries>
</cfchart>

<cfscript>
// config
sSubject = "CFMAIL w/diskless attachment!";
sAddyTo = "to@yourcompany.com";
sAddyFrom = "from@yourcompany.com";

// create the boundary marker indicating each new multi-part section
sBoundary = createUUID();
// create the CID for the embedded image
sCID = "23abc@pc27";
</cfscript>

<cfmail to="#sAddyTo#" from="#sAddyFrom#" subject="#sSubject#">
<cfmailpart type="multipart/mixed; boundary=#chr(34)##sBoundary##chr(34)#">
--#sBoundary#
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit

<html>
<head>
    <title>HTML E-mail</title>
</head>
<body>
    <h1>
        You like using HTML e-mail in CFMX.
    </h1>
    <p>
        <img src=cid:#sCID# />
    </p>
</body>
</html>

--#sBoundary#
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

You like using plain text in CFMX.

--#sBoundary#
Content-Type: application/pdf
Content-Disposition: attachment; filename=test-file.pdf
Content-Transfer-Encoding: Base64

#binaryEncode(binPdf, "base64")#

--#sBoundary#
Content-Type: image/png
Content-Disposition: attachment; filename=chart.png
Content-Transfer-Encoding: Base64

#binaryEncode(binChart, "base64")#

--#sBoundary#
Content-Type: image/png
Content-Disposition: attachment; filename=chart2.png
Content-ID: <#sCID#>
Content-Transfer-Encoding: Base64

#binaryEncode(binChart, "base64")#

--#sBoundary#--
</cfmailpart>
<cfmailpart type="application/x-unsupported">
This should not be rendered by a mail user agent because it does not support the MIME-type.
</cfmailpart>
</cfmail>
Categories: HTML/ColdFusion, Java, Source Code

24 Comments

  • Nice!

    Thanks for sharing.
  • Great looking example. I am going to try it. Do you know of any good examples on processing incoming emails? I am using Version 7 now but soon to get v8. Thanks
  • Dan, this is awesome! I commented a long time ago on your post about sending in-memory binary data using the Java mail API, but that post went pretty high over my head :) This one, however, looks super straight forward. Thanks for showing this demo.
  • @Ben:

    One thing that was pointed out by someone else on CF-Talk was that this method does *not* generate a syntactically correct SMTP message. This means some mail clients may not correctly interpret the message.

    For this reason, I'd suggest still using the Javamail API method.
  • Good to know. Thanks.
  • I am SOOOOO glad you take the time to post information like this for librarians like me who have to do CF on 10 hours/week and absolutely NO programming skills. I got this code to email me a PDF of my list of fields, but it is not filling in the data from my form. Should it be able to do that without external programs? I'm pushing hard to get CF8 here but since we just got 7, I have to make something else work for a while. The form I'm trying to send via email and display as a pdf is at http://www.rochesterpubliclibrary.org/apps/forms/A... if anyone has thoughts for me I would really appreciate it. Once I get one of these going, I can use it for many applications we have at the library.
  • @Susan:

    As a comment above mentions, you would probably be better off using the method talked about here:

    http://blog.pengoworks.com/index.cfm/2007/2/8/Send...

    That should show you off enough code to get you up and running.
  • The embedded image was not displayed when I tried this code. Anyone else had this problem?
  • @Phil:

    As the comments state, this does not produce a syntactically correct SMTP message--which could be your problem.

    The better solution is to use:
    http://blog.pengoworks.com/index.cfm/2007/2/8/Send...

    Also, the CF8.01 allows you to due this natively.
  • Hi,
    This has all been very helpful and enlightening, thanks for posting it!
    But I'm still a little bit stuck. I'd like to send an ics (iCal) file without having to save it as a file. I've been able to create a "file" in memory using the <cfcontent> tag that the user could download from the website. But, what we really want is to send them an email that is an invitation that they could accept or decline. Any suggestions?
    Thanks!
    Ember
  • @Dan
    you said

    "Also, the CF8.01 allows you to due this natively. "

    to clarify are you saying that CF8.01 allows you to use the method seen in this example natively or the method used on the example you linked to?

    thanks in advance,
    tim
  • @Tim:

    In CF 8.01 they introduced new attributes/tags that allow you to send an attachment from memory. See the documentation for more information (I believe its the cfmailparam tag that the functionality was tied to, but can't recall off the top of my head.)
  • thanks for the clarification...i completely overlooked the link to the release notes for 8.01 in the second comment or so on the livedocs site. The answer is there.... CFMAILPARAM added the CONTENT attribute to pass a content variable (i believe making it possible to pass a pdf object created using cfdocument directly to a cfmailparam).

    For those wishing to email an attachment from an uploaded file or a file that only is stored on the sever temporarily they can use the REMOVE="true" attribute on the CFMAILPARAM which will remove the file once the email has cleared the CFMAIL spool. Shown in a great example by Ben Nadel here: http://www.bennadel.com/blog/1214-New-ColdFusion-C...

    Thanks for your quick reply and hopefully this helps someone out after I spent a while searching for something that I felt would have been a grand omission in CF8 in light of all the new PDF capabilities.
  • previous comment directed @Dan..wrong input...whoops :)
  • Dan,

    Nice work. I was struggling to find a solution for some on the fly PDF CFMail attachments. You solution worked like a charm! Thanks for sharing.

    Bob L.
  • I just want to express my love and gratitude! I've spent the last 5 hours or so surfing across expert-exchange, houseoffusion and many many forums looking for a way to email and excel sheet that is dynamically generate and this is absolutely beautiful and worked without much trouble. Only one minor tweak I had to do was replace the binaryEncode(content) with ToBase64(content) because the other threw an error when it encountered a single quote in the content.

    Once again, many many thanks!
    -Amjad
  • Dan, How do I use your code above with my "existing" PDF report?
    - Currently I email the following link to my managers:
    http://192.168.1.77/COE/reports/SalesPipeline_Mgt_...
    - When clicked this opens up the CF Report in PDF format

    Question: How do I alter your code above to use my PDF instead of the other attachments you hard-coded in?

    Thanks again for a great solution for CFMX7
  • @ja:

    First, if you can do this using just CF7 natively provided the file lives on disk:
    http://livedocs.adobe.com/coldfusion/7/htmldocs/00...

    The only problem w/doing this in CF7 is that binary data is not attached to the mail message that lives in the spool--it references the file on disk. So if you have deliver issues on your server w/the CF spool folder, you've got to make sure to not clean up these folders until your sure all e-mail has left the spool folder.

    If using CF7's native solution is going to cause you problems, then you really want to use the Javamail API over this solution--as it'll produce valid messages per the spec. See this blog post for details:

    http://blog.pengoworks.com/index.cfm/2007/2/8/Send...
  • Dan, Got it working using your code above and it works beautifully..! (thanks also to Charlie Arehart's blog that specifically mentioned using cfreport.. missed it)

    Anyway, I deleted the cfdocument tags/code and changed to the following:
    ----------------------------------------------------------
    <cfreport template="SalesPipeline_Mgt_Report_Snapshot.cfr" format="pdf" name="binPdf">
    </cfreport>

    <cfreport template="SalesPipeline_Mgt_Report_Snapshot_Charts.cfr" format="pdf" name="binPdf2">
    </cfreport>
    -----------------------------------------------------------

    I then added in the extra Boundry statement (to handle the 2nd PDF attachment), deleted the code for the cfchart/Image Attachment (not needed)
    and added this CF page to my CFMX7 Scheduler.

    I was amazed at how simple and clean the code ran and delivered the two PDF reports as attachments to my Outlook email Inbox! I'm now looking forward to Scheduling all of my reports in like fashion..

    Thanks again,
    ja
  • Dan, Do you know why these PDF's are not viewable on the iPhone 4 w/ios5?
    - In Outlook 2007 they work perfectly..
    - If forwarded from Outlook to iPhone, they open perfectly..
    - But if sent directly to the iPhone email account, the attachment says "mime-attachment" or winmail.dat file, and when clicked says "Mail cannot open this attachment" ?

    ps: S/MIME on the phone makes no difference.
  • @ja:

    As mentioned in the comments, this does create syntactically correct SMTP message. This means some mail clients may not correctly interpret the message.

    I'd suggest using the JavaMail API--which will create a valid message.

    http://blog.pengoworks.com/index.cfm/2007/2/8/Send...
  • Dan, Using your JavaMail API version, I still cannot get the PDF attachments to show up on the iPhone/iPad?
    - Works perfectly In Outlook 2007..
    - On iPhone I get only text in the subject & body but no attachments at all?

    What is everyone else doing to get view-able PDFs on the iPhone & iPad?
  • If anyone is having trouble getting PDF attachments to be viewable on iPhone & iPad, here is what worked for me:
    - I deleted the cfdocument tags/code and changed to the following:
    ----------------------------------------------------------
    <cfreport template="SalesPipeline_Mgt_Report_Snapshot.cfr" format="pdf" name="binPdf">
    </cfreport>

    <!---// save the file to the following path and file //--->
    <cfset sFilename = expandPath('../archive/SalesPipeline_Mgt_Report_Snapshot.pdf') />

    <!---// write the file to disk //--->
    <cffile action="write" file="#sFilename#" output="#binPdf#">
    -----------------------------------------------------------

    I then added in the following below the <cfmail to line:
    <cfmailparam
        file="SalesPipeline_Mgt_Report_Snapshot.pdf"
        type="application/pdf"
        content="#binPdf#"
        />
    -----------------------------------------------------------
    Next, I deleted all of the contect between the --#sBoundary# and
    --#sBoundary#-- tags

    -----------------------------------------------------------
    Lastly, I deleted the <cfmailpart type="application/x-unsupported">
    This should not be rendered by a mail user agent because it does not support the MIME-type. </cfmailpart>

    Now the PDF's are viewable on the iPhone & iPad, as well as a copy of the report is saved to the archive folder.

    and added this CF page to my CFMX7 Scheduler.
  • Joost van der Drift's Gravatar
    Joost van der Drift
    Taking your example I'm trying to create a mail with an ICS attachment, so that the recipient can acknowledge or decline the invitation.
    Look at http://pastebin.com/3ibpT84s

    But I can not get this to work.

    Any ideas from anyone?

Comments for this entry have been disabled.