This might seem super obvious to some of you, but this small problem had me stumped for a good 10 minutes this morning. I had an XML value that I was converting to Base64 for an HTTP post. Then, on the other side, I needed to take that Base64 encoded value and convert it back to a regular string for use with XML parsing. And then it hit, I had no idea how to, in ColdFusion, take a Base64 encoded value and convert it back to the original string.
Once I figured it out, it was laughably simple:
<!--- Create a string value. ---> <cfset strValue = "Hey there cutie patootie." /> <!--- Convert to base 64. ---> <cfset strBase64Value = ToBase64( strValue ) /> <!--- To convert the base 64 value back to string, simply convert the it a binary representation and then back into to a string. ---> <cfset strNewValue = ToString( ToBinary( strBase64Value ) ) /> <!--- Output test data. ---> <p> <cfoutput> Base 64: #strBase64Value#<br /> Value: #strNewValue# </cfoutput> </p>
Running the above code, we get the following output:
Base 64: SGV5IHRoZXJlIGN1dGllIHBhdG9vdGllLg==
Value: Hey there cutie patootie.
In ColdFusion, the ToBinary() function takes a Base64 encoded value and converts it to its binary representation. This, of course, is a Byte Array. In our example, where each character of a string is represented by a single byte, we have one character per byte array index. Then, calling ToString() on that Byte Array simply converts that character-based byte array back into its string representation.
I've done something similar in a MS SQL Server database - take a text field and output as string:
CAST(CAST(textField AS VARBINARY(1000)) AS VARCHAR(1000))
I think that's how it worked. I'm fairly sure most DBMSs have similar functions.
Earlier this week, I had to do the same thing except that the strValue was a GUID (hex). I found that converting the GUID to decimal and then trying to run ToBase64 on the resulting HUGE number resulted in a CF error. Apparently ToBase64 can only handle decimals up to a certain size. I did find the solution though (with Ray's help) and posted it here: http://www.coldfusionjedi.com/forums/messages.cfm?threadid=2E1AFB13-19B9-E658-9DEC415EB6931B97
Just FYI Ben. The CFML Reference manual is your friend. It's on page 1228.
Good to know.
I assume you needed to go to a decimal first for some reason; otherwise, you can convert any string to Base64 regardless of length.
It's funny, I have gone from String -> Base64 -> Binary so many times that I totally blanked on going the reverse order.
ps. I love the CFML Reference guide :)
Yes. Per my post on coldfusionjedi.com, I needed a *shorter* version of the GUID. If you treat it as a (hex) number, the resulting Base64 has fewer characters/digits than the original:
If you treat the GUID as a string, the resulting Base64 has more characters/digits than the original:
I discovered this phenomenon on Wikipedia, of all places, "When printing fewer characters is desired, GUIDs are sometimes encoded into a base64 or Ascii85 string. Base64-encoded GUID consists of 22 to 24 characters (depending on padding)."
Ahh, yes, that makes sense. Thanks for the clarification.
@John, thanks the hex tip. One thing I've found is the GUIDs are coming back with some 0's missing. Did you notice this?
GUID before: 3D0F1C1C-E3F6-11D3-896A-00105A7027AA
"GUID" after: 3DF1C1CE-3F61-1D38-96A0-105A7027AA
@Jason No, I either didn't notice that or didn't have that problem and, since Sep 2008, my need for this bit of logic went away and the code in question is not easily accessible. Perhaps you can share your code?
@John, It's your original code, I copy/pasted and used it for testing. No biggie, just wondering if it did the same for you. Thx
@Jason My bad. Right there it is at http://www.coldfusionjedi.com/forums/messages.cfm?threadid=2E1AFB13-19B9-E658-9DEC415EB6931B97 No, I didn't notice what you're describing. I was using ACF8. You?
@John, should be the same as yours unless my CF default is somehow different.
@Jason Try these and let me know. I think I might have fixed it and forgotten to update post on Ray's site. If this works, let me know and I will...
<cffunction access="public" name="HexToBase64" output="yes" returntype="string">
<cfargument name="Hex" type="string" required="yes">
<cfset Hex = Replace(Hex, "-", "", "ALL")>
<cfset outStream = CreateObject("java", "java.io.ByteArrayOutputStream").init()>
<cfset inputLength = Len(Hex)>
<cfset outputString = "">
<cfset i = 0>
<cfset ch = "">
<cfif inputLength mod 2 neq 0>
<cfset Hex = "0" & Hex>
<cfloop from="1" to="#inputLength#" index="i" step="2">
<cfset ch = Mid(Hex, i, 2)>
<cfset outStream.write(javacast("int", InputBaseN(ch, 16)))>
<cffunction access="public" name="Base64ToHex" output="yes" returntype="string">
<cfargument name="Base64" type="string" required="yes">
<cfset aBinary = ToBinary(Base64)>
<cfset Hex = "">
<cfloop index="i" from="1" to="#ArrayLen(aBinary)#">
<cfset Hex = Hex & Replace(RJustify(FormatBaseN(BitAnd(aBinary[i], 255), 16), 2), " ", "0", "ALL")>
<cfset Hex = Insert("-", Hex, 20)>
<cfset Hex = Insert("-", Hex, 16)>
<cfset Hex = Insert("-", Hex, 12)>
<cfset Hex = Insert("-", Hex, 8)>
@John, that worked, thanks for the update!
@Jason You're welcome!
How can I check if a string is in Base64 format?
I have a Base64 value (sometimes) like, "IiZQWCAK" and I want to check that to see if it's in base64 format before trying to decode it with Decrypt(ToString(ToBinary(variables.Base64String)), Application.encryptKey). How can I go about accomplishing that?
Any help would be great. Thanks!
I forgot to post the error I get in regards to the above question. If I entered in the value -1 for example, it would throw an error. The error it throws is:
The parameter 1 of function ToBinary, which is now -1 must be a base-64 encoded string.
I need to protect against that somehow.
I know this is an older post, but we have a project where we have to insert images into the DB. The database datatype is Varchar(max) and the images have to be base64 encoded.
This works fine except for transparent PNG's. the alpha channel keeps getting dropped.
We can make it work with a blob, but the developers of the iOS app says that they have to be base64.
So my question is, is it possible with CF to import and image turn it into a blob then encode it to base64 and retain the alpha channel?