Base64编码与解码实现

Table of Contents

问题

java中import sun.misc.BASE64Decoder;提示如下错误:

Access restriction: The type BASE64Decoder is not accessible
  due to restriction on required library

解决方法

可以使用Tomcat6.0中的org.apache.tomcat.util.buf.Base64类。但由于org.apache.tomcat.util.buf.Base64不支持中文的转码,于是在其基础上稍作了一些修改,具体代码如下:

package com.ztools.mail;

public class Base64 {

  static private final int BASELENGTH = 255;
  static private final int LOOKUPLENGTH = 64;
  static private final int TWENTYFOURBITGROUP = 24;
  static private final int EIGHTBIT = 8;
  static private final int SIXTEENBIT = 16;
  static private final int FOURBYTE = 4;

  static private final byte PAD = (byte) '=';
  static private byte[] base64Alphabet = new byte[BASELENGTH];
  static private byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];

  static private final boolean ISDEBUG = true;

  static {

    for (int i = 0; i < BASELENGTH; i++) {
      base64Alphabet[i] = -1;
    }
    // A-Z 0-25
    for (int i = 'Z'; i >= 'A'; i--) {
      base64Alphabet[i] = (byte) (i - 'A');
    }
    // a-z 26-51
    for (int i = 'z'; i >= 'a'; i--) {
      base64Alphabet[i] = (byte) (i - 'a' + 26);
    }

    // 1-9 52-61
    for (int i = '9'; i >= '0'; i--) {
      base64Alphabet[i] = (byte) (i - '0' + 52);
    }

    base64Alphabet['+'] = 62;
    base64Alphabet['/'] = 63;

    for (int i = 0; i <= 25; i++)
      lookUpBase64Alphabet[i] = (byte) ('A' + i);

    for (int i = 26, j = 0; i <= 51; i++, j++)
      lookUpBase64Alphabet[i] = (byte) ('a' + j);

    for (int i = 52, j = 0; i <= 61; i++, j++)
      lookUpBase64Alphabet[i] = (byte) ('0' + j);

    lookUpBase64Alphabet[62] = (byte) '+';
    lookUpBase64Alphabet[63] = (byte) '/';
  }

  static boolean isBase64(byte octect) {
    // shall we ignore white space? JEFF??
    return (octect == PAD || base64Alphabet[octect] != -1);
  }

  static boolean isArrayByteBase64(byte[] arrayOctect) {
    int length = arrayOctect.length;
    if (length == 0)
      return false;
    for (int i = 0; i < length; i++) {
      if (Base64.isBase64(arrayOctect[i]) == false)
        return false;
    }
    return true;
  }

  /**
   * Encodes hex octects into Base64
   * 
   * @param binaryData
   *          Array containing binaryData
   * @return Encoded Base64 array
   */
  public static byte[] encode(byte[] binaryData) {
    int lengthDataBits = binaryData.length * EIGHTBIT;
    int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
    int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
    byte encodedData[] = null;

    if (fewerThan24bits != 0) // data not divisible by 24 bit
      encodedData = new byte[(numberTriplets + 1) * 4];
    else
      // 16 or 8 bit
      encodedData = new byte[numberTriplets * 4];

    byte k = 0, l = 0, oldByte1 = 0, oldByte2 = 0, oldByte3 = 0;

    int encodedIndex = 0;
    int dataIndex = 0;
    int i = 0;
    for (i = 0; i < numberTriplets; i++) {

      dataIndex = i * 3;
      oldByte1 = binaryData[dataIndex];
      oldByte2 = binaryData[dataIndex + 1];
      oldByte3 = binaryData[dataIndex + 2];

      l = (byte) (oldByte2 & 0x0f);
      k = (byte) (oldByte1 & 0x03);

      // new bytes (three bytes to four bytes)
      encodedIndex = i * 4;
      encodedData[encodedIndex] = lookUpBase64Alphabet[oldByte1 >> 2 & 0x3f];
      encodedData[encodedIndex + 1] = lookUpBase64Alphabet[(oldByte2 >> 4 & 0xf)
          | (k << 4)];
      encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2)
          | (oldByte3 >> 6 & 0x3)];
      encodedData[encodedIndex + 3] = lookUpBase64Alphabet[oldByte3 & 0x3f];
    }

    // form integral number of 6-bit groups
    dataIndex = i * 3;
    encodedIndex = i * 4;
    if (fewerThan24bits == EIGHTBIT) {
      oldByte1 = binaryData[dataIndex];
      k = (byte) (oldByte1 & 0x03);
      encodedData[encodedIndex] = lookUpBase64Alphabet[oldByte1 >> 2 & 0x3f];
      encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
      encodedData[encodedIndex + 2] = PAD;
      encodedData[encodedIndex + 3] = PAD;
    } else if (fewerThan24bits == SIXTEENBIT) {

      oldByte1 = binaryData[dataIndex];
      oldByte2 = binaryData[dataIndex + 1];
      l = (byte) (oldByte2 & 0x0f);
      k = (byte) (oldByte1 & 0x03);
      encodedData[encodedIndex] = lookUpBase64Alphabet[oldByte1 >> 2 & 0x3f];
      encodedData[encodedIndex + 1] = lookUpBase64Alphabet[(oldByte2 >> 4 & 0xf)
          | (k << 4)];
      encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
      encodedData[encodedIndex + 3] = PAD;
    }
    return encodedData;
  }

  public static String encode(String str) {
    byte[] binaryData = str.getBytes();
    return new String(encode(binaryData));
  }

  /**
   * Decodes Base64 data into octects
   * 
   * @param base64Data
   *          Byte array containing Base64 data
   * @return Array containind decoded data.
   */
  public static byte[] decode(byte[] base64Data) {
    int numberQuadruple = base64Data.length / FOURBYTE;
    byte decodedData[] = null;
    byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;

    // Throw away anything not in base64Data
    // Adjust size

    int encodedIndex = 0;
    int dataIndex = 0;
    decodedData = new byte[numberQuadruple * 3 + 1];

    for (int i = 0; i < numberQuadruple; i++) {
      dataIndex = i * 4;
      marker0 = base64Data[dataIndex + 2];
      marker1 = base64Data[dataIndex + 3];

      b1 = base64Alphabet[base64Data[dataIndex]];
      b2 = base64Alphabet[base64Data[dataIndex + 1]];

      if (marker0 != PAD && marker1 != PAD) { // No PAD e.g 3cQl
        b3 = base64Alphabet[marker0];
        b4 = base64Alphabet[marker1];

        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
        decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
      } else if (marker0 == PAD) { // Two PAD e.g. 3c[Pad][Pad]
        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        decodedData[encodedIndex + 1] = (byte) ((b2 & 0xf) << 4);
        decodedData[encodedIndex + 2] = (byte) 0;
      } else if (marker1 == PAD) { // One PAD e.g. 3cQ[Pad]
        b3 = base64Alphabet[marker0];

        decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
        decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
        decodedData[encodedIndex + 2] = (byte) (b3 << 6);
      }
      encodedIndex += 3;
    }

    // NNN000 to NNN (clear end zero)
    int retDateLength = decodedData.length;
    for (int i = retDateLength - 1; i >= 0; i--) {
      if (0 != decodedData[i]) {
        break;
      }
      retDateLength--;
    }

    byte[] retDate = new byte[retDateLength];
    System.arraycopy(decodedData, 0, retDate, 0, retDateLength);
    return retDate;

  }

  public static String decode(String base64String) {
    char[] cs = base64String.toCharArray();
    byte[] bs = new byte[cs.length];

    for (int i = 0; i < cs.length; i++) {
      bs[i] = (byte) cs[i];
    }
    return new String(decode(bs));
  }

  static final int base64[] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
      52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1,
      2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
      22, 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33,
      34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
      64, 64, 64, 64, 64, 64, 64 };

  public static String base64Decode(String orig) {
    char chars[] = orig.toCharArray();
    StringBuffer sb = new StringBuffer();
    int i = 0;

    int shift = 0; // # of excess bits stored in accum
    int acc = 0;

    for (i = 0; i < chars.length; i++) {
      int v = base64[chars[i] & 0xFF];

      if (v >= 64) {
        if (chars[i] != '=')
          if (ISDEBUG)
            System.out.println(("Wrong char in base64: " + chars[i]));
      } else {
        acc = (acc << 6) | v;
        shift += 6;
        if (shift >= 8) {
          shift -= 8;
          sb.append((char) ((acc >> shift) & 0xff));
        }
      }
    }
    return sb.toString();
  }

}