11

I have a common library that I use for both Java and Android projects and it requires a base64 encoder/decoder. The trouble is, the Apache commons library does not work with Android, at least not that I have been able to successfully implement - due to Android already implementing and earlier version and thus causing an error at run time whenever I attempt to encode or decode:

Base64.decodeBase64

Returns the error:

AndroidRuntime(1420): java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.decodeBase64

If anyone knows of a base64 library that is compatible with both Java and Android, or can explain to me how to get around the Apache commons issue, I would be very grateful. :^)

Roy Hinkley
  • 10,111
  • 21
  • 80
  • 120
  • You sure you're just not including the Apache Commons Codec library? The method in question has been in there for years. – Sean Owen Nov 25 '12 at 23:52
  • Yes, I was including it. There is a conflict in Android using version 5 or higher. These methods do not exist before version 5 - as I understand it and Android is using version 4. – Roy Hinkley Nov 25 '12 at 23:55
  • And if you don't believe me, then try it for yourself and prove me wrong. Using Apache commons v1.7 I get the following error --> AndroidRuntime(1420): java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.decodeBase64 – Roy Hinkley Nov 26 '12 at 00:37
  • For goodness' sake, as webnet says, learn the simple algorithm and write your own. You can even write it in Javascript. – Blessed Geek Nov 26 '12 at 00:52
  • So why can't you use the methods that *are* present in 4? – user207421 Nov 26 '12 at 01:16
  • Guava will have one in release 14.0, though it's not out yet. – Louis Wasserman Nov 26 '12 at 04:59

3 Answers3

11

There's just "base64". It so trivial that you can google for any "java base64" and use any implementation.

EDIT

If you target pre API8, simply grab the source from Base64 implementation from API8 (it is android/util/Base64.java) and copy into your project.

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • If I could use any implementation, then the Apache commons would work, so would the sun.misc.BASE64Decoder, but it does not either. I only want a library from a trusted source. – Roy Hinkley Nov 26 '12 at 00:19
  • 2
    So just use Android sources and put it into your project. I added link to my edited answer – Marcin Orlowski Nov 26 '12 at 00:43
  • agree with WebnetMobile.com, copy the Base64.java from android source and put it in your project. – Win Myo Htet Nov 26 '12 at 01:19
  • 1
    If you have a library that depends on a Base64 class you probably need to fork the library and update the references. Chances are it's trying to import `foo.bar.Base64` when you have `com.yourapplication.util.Base64`. – twaddington Nov 26 '12 at 06:38
  • Okay I got the source to work in Android and am currently verifying in my J2EE apps now. Will mark as answer if it works. Muchas Gracias if this works!!! – Roy Hinkley Nov 27 '12 at 14:17
2

Roll your own.

Here's decoding:

      static private int FromBase64Char(int c)
{
    if(c >= 'A' && c <= 'Z')
        return c - 'A';
    else if(c >= 'a' && c <= 'z')
        return c - 'a' + 26;
    else if(c >= '0' && c <= '9')
        return c - '0' + 52;
    else if(c == '+')
        return 62;
    else if(c == '/')
        return 63;
    else
        throw new IllegalArgumentException(); //Depends on how do you want to handle invalid characters
}


static public byte[] FromBase64(String s) throws IllegalArgumentException
{
    if(s == null)
        return null;

    int l = s.length();
    if(l == 0)
        return new byte[0];

    if(l % 4 != 0)
        throw new IllegalArgumentException();

    boolean Padded = (s.charAt(l-1) == '=');
    boolean Padded2 = (s.charAt(l-2) == '=');
    int ll = (Padded ? l-4 : l);
    int triad;

    byte [] b = new byte[(ll*3)/4 + (Padded ? (Padded2 ? 1 : 2) : 0)];

    int i, j = 0;
    for(i=0; i<ll; i+=4)
    {
        triad = 
            (FromBase64Char(s.charAt(i)) << 18) |
            (FromBase64Char(s.charAt(i+1)) << 12) |
            (FromBase64Char(s.charAt(i+2)) << 6) |
            FromBase64Char(s.charAt(i+3));

        b[j++] = (byte)((triad >> 16) & 0xff); 
        b[j++] = (byte)((triad >> 8) & 0xff);
        b[j++] = (byte)(triad & 0xff);
    }
    //The final chunk
    if(Padded)
    {
        if(Padded2) //Padded with two ='s
        {
            triad = (FromBase64Char(s.charAt(ll)) <<2 ) | (FromBase64Char(s.charAt(ll+1)) >> 4);
            b[j++] = (byte)triad;
        }
        else //Padded with one =
        {
            triad =
                (FromBase64Char(s.charAt(ll)) << 10) |
                (FromBase64Char(s.charAt(ll+1)) << 4) |
                (FromBase64Char(s.charAt(ll+2)) >> 2);  
            b[j++] = (byte)((triad >> 8) & 0xff);
            b[j++] = (byte)(triad & 0xff);
        }
    }
    return b;
}

And here's encoding:

private final static String BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
        PAD1 = "=", PAD2 = "==";

static public String ToBase64(final byte [] a)
{
    int l = a.length;
    StringBuilder sb = new StringBuilder((l+3)*4/3);
    int i;
    int mod = l % 3;
    int ll = l - mod;
    int triad;
    for(i=0;i<ll;i+=3)
    {
        triad = (a[i]<<16) | (a[i+1]<<8) | a[i+2];
        sb.append(BASE64_ALPHABET.charAt((triad >> 18) & 0x3f));
        sb.append(BASE64_ALPHABET.charAt((triad >> 12) & 0x3f));
        sb.append(BASE64_ALPHABET.charAt((triad >> 6) & 0x3f));
        sb.append(BASE64_ALPHABET.charAt(triad & 0x3f));
    }
    if(mod == 1)
    {
        sb.append(BASE64_ALPHABET.charAt((a[i] >> 2) & 0x3f));
        sb.append(BASE64_ALPHABET.charAt((a[i] << 4) & 0x3f));
        sb.append(PAD2);
    }
    if(mod == 2)
    {
        triad = (a[i]<<8) | a[i+1];
        sb.append(BASE64_ALPHABET.charAt((triad >> 10) & 0x3ff));
        sb.append(BASE64_ALPHABET.charAt((triad >> 4) & 0x3f));
        sb.append(BASE64_ALPHABET.charAt((triad << 2) & 0x3f));
        sb.append(PAD1);        
    }
    return sb.toString();   
}
Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
0

In my application, I use jackson and it provides a base64 encoder/decoder, you can use it if your application use also Jackson.

import com.fasterxml.jackson.core.Base64Variants;

public class Foo {
    String encodeBase64(byte[] string) {
        return Base64Variants.getDefaultVariant().encode(string.getBytes());
    }
}
Nelson G.
  • 5,145
  • 4
  • 43
  • 54