Performs Base64 encoding and/or decoding : Base64 « Development Class « Java






Performs Base64 encoding and/or decoding

      
/*
 * Copyright 1999,2005 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.UndeclaredThrowableException;

import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;


/** Performs Base64 encoding and/or decoding. This is an on-the-fly decoder: Unlike,
 * for example, the commons-codec classes, it doesn't depend on byte arrays. In
 * other words, it has an extremely low memory profile. This is well suited even
 * for very large byte streams.
 */
public class Base64 {
  /** An exception of this type is thrown, if the decoded
   * character stream contains invalid input.
   */
  public static class DecodingException extends IOException {
    private static final long serialVersionUID = 3257006574836135478L;
    DecodingException(String pMessage) { super(pMessage); }
  }

  /** An exception of this type is thrown by the {@link SAXEncoder},
   * if writing to the target handler causes a SAX exception.
   * This class is required, because the {@link IOException}
   * allows no cause until Java 1.3.
   */
  public static class SAXIOException extends IOException {
    private static final long serialVersionUID = 3258131345216451895L;
    final SAXException saxException;
    SAXIOException(SAXException e) {
      super();
      saxException = e;
    }
    /** Returns the encapsulated {@link SAXException}.
     * @return An exception, which was thrown when invoking
     * {@link ContentHandler#characters(char[], int, int)}.
     */
    public SAXException getSAXException() { return saxException; }
  }


  /** Default line separator: \n
   */
  public static final String LINE_SEPARATOR = "\n";

  /** Default size for line wrapping.
   */
  public static final int LINE_SIZE = 76;

  /**
     * This array is a lookup table that translates 6-bit positive integer
     * index values into their "Base64 Alphabet" equivalents as specified 
     * in Table 1 of RFC 2045.
     */
    private static final char intToBase64[] = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
    };

    /**
     * This array is a lookup table that translates unicode characters
     * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
     * into their 6-bit positive integer equivalents.  Characters that
     * are not in the Base64 alphabet but fall within the bounds of the
     * array are translated to -1.
     */
    private static final byte base64ToInt[] = {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
        55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1, -1, 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
    };

  /** An encoder is an object, which is able to encode byte array
   * in blocks of three bytes. Any such block is converted into an
   * array of four bytes.
   */
  public static abstract class Encoder {
    private int num, numBytes;
    private final char[] charBuffer;
    private int charOffset;
    private final int wrapSize;
    private final int skipChars;
    private final String sep;
    private int lineChars = 0;
    /** Creates a new instance.
     * @param pBuffer The encoders buffer. The encoder will
     * write to the buffer as long as possible. If the
     * buffer is full or the end of data is signaled, then
     * the method {@link #writeBuffer(char[], int, int)}
     * will be invoked.
     * @param pWrapSize A nonzero value indicates, that a line
     * wrap should be performed after the given number of
     * characters. The value must be a multiple of 4. Zero
     * indicates, that no line wrap should be performed.
     * @param pSep The eol sequence being used to terminate
     * a line in case of line wraps. May be null, in which
     * case the default value {@link Base64#LINE_SEPARATOR}
     * is being used.
     */
    protected Encoder(char[] pBuffer, int pWrapSize, String pSep) {
      charBuffer = pBuffer;
      sep = pSep == null ? null : Base64.LINE_SEPARATOR;
      skipChars = pWrapSize == 0 ? 4 : 4 + sep.length();
      wrapSize = skipChars == 4 ? 0 : pWrapSize;
      if (wrapSize < 0  ||  wrapSize %4 > 0) {
        throw new IllegalArgumentException("Illegal argument for wrap size: " + pWrapSize
                           + "(Expected nonnegative multiple of 4)");
      }
      if (pBuffer.length < skipChars) {
        throw new IllegalArgumentException("The buffer must contain at least " + skipChars
                           + " characters, but has " + pBuffer.length);
      }
    }
    /** Called for writing the buffer contents to the target.
     * @param pChars The buffer being written.
     * @param pOffset Offset of first character being written.
     * @param pLen Number of characters being written.
     * @throws IOException Writing to the destination failed.
     */
    protected abstract void writeBuffer(char[] pChars, int pOffset, int pLen) throws IOException;

    private void wrap() {
      for (int j = 0;  j < sep.length();  j++) {
        charBuffer[charOffset++] = sep.charAt(j);
      }
      lineChars = 0;
    }

    /** Encodes the given byte array.
     * @param pBuffer Byte array being encoded.
     * @param pOffset Offset of first byte being encoded.
     * @param pLen Number of bytes being encoded.
     * @throws IOException Invoking the {@link #writeBuffer(char[],int,int)} method
     * for writing the encoded data failed.
     */
    public void write(byte[] pBuffer, int pOffset, int pLen) throws IOException {
      for(int i = 0;  i < pLen;  i++) {
        int b = pBuffer[pOffset++];
        if (b < 0) { b += 256; }
        num = (num << 8) + b;
        if (++numBytes == 3) {
          charBuffer[charOffset++] = intToBase64[num >> 18];
          charBuffer[charOffset++] = intToBase64[(num >> 12) & 0x3f];
          charBuffer[charOffset++] = intToBase64[(num >> 6) & 0x3f];
          charBuffer[charOffset++] = intToBase64[num & 0x3f];
          if (wrapSize > 0) {
            lineChars += 4;
            if (lineChars >= wrapSize) {
              wrap();
            }
          }
          num = 0;
          numBytes = 0;
          if (charOffset + skipChars > charBuffer.length) {
            writeBuffer(charBuffer, 0, charOffset);
            charOffset = 0;
          }
        }
      }
    }
    /** Writes any currently buffered data to the destination.
     * @throws IOException Invoking the {@link #writeBuffer(char[],int,int)}
     * method for writing the encoded data failed.
     */
    public void flush() throws IOException {
      if (numBytes > 0) {
        if (numBytes == 1) {
          charBuffer[charOffset++] = intToBase64[num >> 2];
          charBuffer[charOffset++] = intToBase64[(num << 4) & 0x3f];
          charBuffer[charOffset++] = '=';
          charBuffer[charOffset++] = '=';
        } else {
          charBuffer[charOffset++] = intToBase64[num >> 10];
          charBuffer[charOffset++] = intToBase64[(num >> 4) & 0x3f];
          charBuffer[charOffset++] = intToBase64[(num << 2) & 0x3f];
          charBuffer[charOffset++] = '=';
        }
        lineChars += 4;
        num = 0;
        numBytes = 0;
      }
      if (wrapSize > 0  &&  lineChars > 0) {
        wrap();
      }
      if (charOffset > 0) {
        writeBuffer(charBuffer, 0, charOffset);
        charOffset = 0;
      }
    }
  }

  /** An {@link OutputStream}, which is writing to the given
   * {@link Encoder}.
   */
  public static class EncoderOutputStream extends OutputStream {
    private final Encoder encoder;
    /** Creates a new instance, which is creating
     * output using the given {@link Encoder}.
     * @param pEncoder The base64 encoder being used.
     */
    public EncoderOutputStream(Encoder pEncoder) {
      encoder = pEncoder;
    }
    private final byte[] oneByte = new byte[1];
    public void write(int b) throws IOException {
      oneByte[0] = (byte) b;
      encoder.write(oneByte, 0, 1);
    }
    public void write(byte[] pBuffer, int pOffset, int pLen) throws IOException {
      encoder.write(pBuffer, pOffset, pLen);
    }
    public void close() throws IOException {
      encoder.flush();
    }
  }

  /** Returns an {@link OutputStream}, that encodes its input in Base64
   * and writes it to the given {@link Writer}. If the Base64 stream
   * ends, then the output streams {@link OutputStream#close()} method
   * must be invoked. Note, that this will <em>not</em> close the
   * target {@link Writer}!
   * @param pWriter Target writer.
   * @return An output stream, encoding its input in Base64 and writing
   * the output to the writer <code>pWriter</code>.
   */
  public static OutputStream newEncoder(Writer pWriter) {
    return newEncoder(pWriter, LINE_SIZE, LINE_SEPARATOR);
  }

  /** Returns an {@link OutputStream}, that encodes its input in Base64
   * and writes it to the given {@link Writer}. If the Base64 stream
   * ends, then the output streams {@link OutputStream#close()} method
   * must be invoked. Note, that this will <em>not</em> close the
   * target {@link Writer}!
   * @param pWriter Target writer.
   * @param pLineSize Size of one line in characters, must be a multiple
   * of four. Zero indicates, that no line wrapping should occur.
   * @param pSeparator Line separator or null, in which case the default value
   * {@link #LINE_SEPARATOR} is used.
   * @return An output stream, encoding its input in Base64 and writing
   * the output to the writer <code>pWriter</code>.
   */
  public static OutputStream newEncoder(final Writer pWriter, int pLineSize, String pSeparator) {
    final Encoder encoder = new Encoder(new char[4096], pLineSize, pSeparator){
      protected void writeBuffer(char[] pBuffer, int pOffset, int pLen) throws IOException {
        pWriter.write(pBuffer, pOffset, pLen);
      }
    };
    return new EncoderOutputStream(encoder);
  }

  /** An {@link Encoder}, which is writing to a SAX content handler.
   * This is typically used for embedding a base64 stream into an
   * XML document.
   */
  public static class SAXEncoder extends Encoder {
    private final ContentHandler handler;
    /** Creates a new instance.
     * @param pBuffer The encoders buffer.
     * @param pWrapSize A nonzero value indicates, that a line
     * wrap should be performed after the given number of
     * characters. The value must be a multiple of 4. Zero
     * indicates, that no line wrap should be performed.
     * @param pSep The eol sequence being used to terminate
     * a line in case of line wraps. May be null, in which
     * case the default value {@link Base64#LINE_SEPARATOR}
     * is being used.
     * @param pHandler The target handler.
     */
    public SAXEncoder(char[] pBuffer, int pWrapSize, String pSep,
              ContentHandler pHandler) {
      super(pBuffer, pWrapSize, pSep);
      handler = pHandler;
    }
    /** Writes to the content handler.
     * @throws SAXIOException Writing to the content handler
     * caused a SAXException.
     */
    protected void writeBuffer(char[] pChars, int pOffset, int pLen) throws IOException {
      try {
        handler.characters(pChars, pOffset, pLen);
      } catch (SAXException e) {
        throw new SAXIOException(e);
      }
    }
  }

  /** Converts the given byte array into a base64 encoded character
   * array.
   * @param pBuffer The buffer being encoded.
   * @param pOffset Offset in buffer, where to begin encoding.
   * @param pLength Number of bytes being encoded.
   * @return Character array of encoded bytes.
   */
  public static String encode(byte[] pBuffer, int pOffset, int pLength) {
    return encode(pBuffer, pOffset, pLength, LINE_SIZE, LINE_SEPARATOR);
  }

  /** Converts the given byte array into a base64 encoded character
   * array.
   * @param pBuffer The buffer being encoded.
   * @param pOffset Offset in buffer, where to begin encoding.
   * @param pLength Number of bytes being encoded.
   * @param pLineSize Size of one line in characters, must be a multiple
   * of four. Zero indicates, that no line wrapping should occur.
   * @param pSeparator Line separator or null, in which case the default value
   * {@link #LINE_SEPARATOR} is used.
   * @return Character array of encoded bytes.
   */
  public static String encode(byte[] pBuffer, int pOffset, int pLength,
                int pLineSize, String pSeparator) {
    StringWriter sw = new StringWriter();
    OutputStream ostream = newEncoder(sw, pLineSize, pSeparator);
    try {
      ostream.write(pBuffer, pOffset, pLength);
      ostream.close();
    } catch (IOException e) {
      throw new UndeclaredThrowableException(e);
    }
    return sw.toString();
  }

  /** Converts the given byte array into a base64 encoded character
   * array with the line size {@link #LINE_SIZE} and the separator
   * {@link #LINE_SEPARATOR}.
   * @param pBuffer The buffer being encoded.
   * @return Character array of encoded bytes.
   */
  public static String encode(byte[] pBuffer) {
    return encode(pBuffer, 0, pBuffer.length);
  }

  /** An encoder is an object, which is able to decode char arrays
   * in blocks of four bytes. Any such block is converted into a
   * array of three bytes.
   */
  public static abstract class Decoder {
    private final byte[] byteBuffer;
    private int byteBufferOffset;
    private int num, numBytes;
    private int eofBytes;
    /** Creates a new instance.
     * @param pBufLen The decoders buffer size. The decoder will
     * store up to this number of decoded bytes before invoking
     * {@link #writeBuffer(byte[],int,int)}.
     */
    protected Decoder(int pBufLen) {
      byteBuffer = new byte[pBufLen];
    }
    /** Called for writing the decoded bytes to the destination.
     * @param pBuffer The byte array being written.
     * @param pOffset Offset of the first byte being written.
     * @param pLen Number of bytes being written.
     * @throws IOException Writing to the destination failed.
     */
    protected abstract void writeBuffer(byte[] pBuffer, int pOffset, int pLen) throws IOException;
    /** Converts the Base64 encoded character array.
     * @param pData The character array being decoded.
     * @param pOffset Offset of first character being decoded.
     * @param pLen Number of characters being decoded.
     * @throws DecodingException Decoding failed.
     * @throws IOException An invocation of the {@link #writeBuffer(byte[],int,int)}
     * method failed.
     */
    public void write(char[] pData, int pOffset, int pLen) throws IOException {
      for (int i = 0;  i < pLen;  i++) {
        char c = pData[pOffset++];
        if (Character.isWhitespace(c)) {
          continue;
        }
        if (c == '=') {
          ++eofBytes;
          num = num << 6;
          switch(++numBytes) {
            case 1:
            case 2:
              throw new DecodingException("Unexpected end of stream character (=)");
            case 3:
              // Wait for the next '='
              break;
            case 4:
              byteBuffer[byteBufferOffset++] = (byte) (num >> 16);
              if (eofBytes == 1) {
                byteBuffer[byteBufferOffset++] = (byte) (num >> 8);
              }
              writeBuffer(byteBuffer, 0, byteBufferOffset);
              byteBufferOffset = 0;
              break;
            case 5:
              throw new DecodingException("Trailing garbage detected");
            default:
              throw new IllegalStateException("Invalid value for numBytes");
          }
        } else {
          if (eofBytes > 0) {
            throw new DecodingException("Base64 characters after end of stream character (=) detected.");
          }
          int result;
          if (c >= 0  &&  c < base64ToInt.length) {
            result = base64ToInt[c];
            if (result >= 0) {
              num = (num << 6) + result;
              if (++numBytes == 4) {
                byteBuffer[byteBufferOffset++] = (byte) (num >> 16);
                byteBuffer[byteBufferOffset++] = (byte) ((num >> 8) & 0xff);
                byteBuffer[byteBufferOffset++] = (byte) (num & 0xff);
                if (byteBufferOffset + 3 > byteBuffer.length) {
                  writeBuffer(byteBuffer, 0, byteBufferOffset);
                  byteBufferOffset = 0;
                }
                num = 0;
                numBytes = 0;
              }
              continue;
            }
            }
          if (!Character.isWhitespace(c)) {
            throw new DecodingException("Invalid Base64 character: " + (int) c);
          }
        }
      }
    }
    /** Indicates, that no more data is being expected. Writes all currently
     * buffered data to the destination by invoking {@link #writeBuffer(byte[],int,int)}.
     * @throws DecodingException Decoding failed (Unexpected end of file).
     * @throws IOException An invocation of the {@link #writeBuffer(byte[],int,int)} method failed.
     */
    public void flush() throws IOException {
      if (numBytes != 0  &&  numBytes != 4) {
        throw new DecodingException("Unexpected end of file");
      }
      if (byteBufferOffset > 0) {
        writeBuffer(byteBuffer, 0, byteBufferOffset);
        byteBufferOffset = 0;
      }
    }
  }

  /** Returns a {@link Writer}, that decodes its Base64 encoded
   * input and writes it to the given {@link OutputStream}.
   * Note, that the writers {@link Writer#close()} method will
   * <em>not</em> close the output stream <code>pStream</code>!
   * @param pStream Target output stream.
   * @return An output stream, encoding its input in Base64 and writing
   * the output to the writer <code>pWriter</code>.
   */
  public Writer newDecoder(final OutputStream pStream) {
    return new Writer(){
      private final Decoder decoder = new Decoder(1024){
        protected void writeBuffer(byte[] pBytes, int pOffset, int pLen) throws IOException {
          pStream.write(pBytes, pOffset, pLen);
        }
      };
      public void close() throws IOException {
        flush();
      }
      public void flush() throws IOException {
        decoder.flush();
        pStream.flush();
      }
      public void write(char[] cbuf, int off, int len) throws IOException {
        decoder.write(cbuf, off, len);
      }
    };
  }

  /** Converts the given base64 encoded character buffer into a byte array.
   * @param pBuffer The character buffer being decoded.
   * @param pOffset Offset of first character being decoded.
   * @param pLength Number of characters being decoded.
   * @return Converted byte array
   * @throws DecodingException The input character stream contained invalid data.
   */
  public static byte[] decode(char[] pBuffer, int pOffset, int pLength) throws DecodingException {
    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Decoder d = new Decoder(1024){
      protected void writeBuffer(byte[] pBuf, int pOff, int pLen) throws IOException {
        baos.write(pBuf, pOff, pLen);
      }
    };
    try {
      d.write(pBuffer, pOffset, pLength);
      d.flush();
    } catch (DecodingException e) {
      throw e;
    } catch (IOException e) {
      throw new UndeclaredThrowableException(e);
    }
    return baos.toByteArray();
  }

  /** Converts the given base64 encoded character buffer into a byte array.
   * @param pBuffer The character buffer being decoded.
   * @return Converted byte array
   * @throws DecodingException The input character stream contained invalid data.
   */
  public static byte[] decode(char[] pBuffer) throws DecodingException {
    return decode(pBuffer, 0, pBuffer.length);
  }

  /** Converts the given base64 encoded String into a byte array.
   * @param pBuffer The string being decoded.
   * @return Converted byte array
   * @throws DecodingException The input character stream contained invalid data.
   */
  public static byte[] decode(String pBuffer) throws DecodingException {
    return decode(pBuffer.toCharArray());
  }
}

   
    
    
    
    
    
  








Related examples in the same category

1.Base64 encoding/decoding.
2.Decodes Base64 data into octects
3.Implementation of MIME's Base64 encoding and decoding conversions.
4.Encode/decode for RFC 2045 Base64 as defined by RFC 2045
5.Encode/decode for RFC 2045 Base64 as defined by RFC 2045, N. Freed and N. Borenstein.
6.Encodes and decodes to and from Base64 notation.
7.Encodes hex octects into Base64
8.Helper class to provide Base64 encoding routines.
9.Represents a collection of 64 boolean (on/off) flags.
10.byte to be tested if it is Base64 alphabet
11.to Base64
12.One of the fastest implementation of the Base64 encoding. Jakarta and others are slower
13.array of byte to encode
14.Codes number up to radix 62
15.A Base64 Encoder/Decoder
16.A fast and memory efficient class to encode and decode to and from BASE64 in full accordance with RFC 2045
17.BASE64 encoder implementation
18.Base-64 Encoder - translates from base-64 text into binary
19.Base64 Character encoder as specified in RFC1113
20.Base64 Utils
21.Base64 encoder/decoder
22.Base64 from by Funambol, Inc.
23.Convert to hex from byte arrays and back
24.Converting hexadecimal strings
25.Encode and decode data in Base64 format as described in RFC 1521
26.Encode and decode integers, times, and internationalized strings to and from popular binary formats
27.Encoding of raw bytes to base64-encoded characters, and decoding of base64 characters to raw bytes
28.Provides Base64 encoding and decoding as defined by RFC 2045
29.Provides Base64 encoding and decoding with URL and filename safe alphabet as defined by RFC 3548, section 4.
30.Provides utility methods to Base64 encode data
31.QP Decoder Stream
32.QP Encoder Stream
33.A class to decode Base64 streams and strings.
34.A class to encode Base64 streams and strings.
35.Encodes binary data to plain text as Base64
36.A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance with RFC 2045.
37.Decodes InputStreams which contain binary data in base64 form
38.Base 64 Converter
39.Base64 from org.cspoker.common.util
40.Base64 converted from code at http://iharder.sourceforge.net/base64/
41.Encodes and decodes to and from Base64 notation.
42.Simple Base64 string decoding function
43.Class to represent unsigned 64-bit numbers.
44.A Base64 encoder/decoder.
45.The Base64 utility class implements Base-64 and Base-85 encoding and decoding algorithms.
46.Provides Base64 encoding and decoding
47.Code to read and write Base64-encoded text.
48.Base32 encoding/decoding class.