package com.loopj.android.http; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; public class Base64OutputStream extends FilterOutputStream { private final Base64.Coder coder; private final int flags; private byte[] buffer = null; private int bpos = 0; private static byte[] EMPTY = new byte[0]; /** * Performs Base64 encoding on the data written to the stream, writing the encoded data to * another OutputStream. * * @param out the OutputStream to write the encoded data to * @param flags bit flags for controlling the encoder; see the constants in {@link Base64} */ public Base64OutputStream(OutputStream out, int flags) { this(out, flags, true); } /** * Performs Base64 encoding or decoding on the data written to the stream, writing the * encoded/decoded data to another OutputStream. * * @param out the OutputStream to write the encoded data to * @param flags bit flags for controlling the encoder; see the constants in {@link Base64} * @param encode true to encode, false to decode */ public Base64OutputStream(OutputStream out, int flags, boolean encode) { super(out); this.flags = flags; if (encode) { coder = new Base64.Encoder(flags, null); } else { coder = new Base64.Decoder(flags, null); } } public void write(int b) throws IOException { // To avoid invoking the encoder/decoder routines for single // bytes, we buffer up calls to write(int) in an internal // byte array to transform them into writes of decently-sized // arrays. if (buffer == null) { buffer = new byte[1024]; } if (bpos >= buffer.length) { // internal buffer full; write it out. internalWrite(buffer, 0, bpos, false); bpos = 0; } buffer[bpos++] = (byte) b; } /** * Flush any buffered data from calls to write(int). Needed before doing a write(byte[], int, * int) or a close(). */ private void flushBuffer() throws IOException { if (bpos > 0) { internalWrite(buffer, 0, bpos, false); bpos = 0; } } public void write(byte[] b, int off, int len) throws IOException { if (len <= 0) return; flushBuffer(); internalWrite(b, off, len, false); } public void close() throws IOException { IOException thrown = null; try { flushBuffer(); internalWrite(EMPTY, 0, 0, true); } catch (IOException e) { thrown = e; } try { if ((flags & Base64.NO_CLOSE) == 0) { out.close(); } else { out.flush(); } } catch (IOException e) { if (thrown != null) { thrown = e; } } if (thrown != null) { throw thrown; } } /** * Write the given bytes to the encoder/decoder. * * @param finish true if this is the last batch of input, to cause encoder/decoder state to be * finalized. */ private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException { coder.output = embiggen(coder.output, coder.maxOutputSize(len)); if (!coder.process(b, off, len, finish)) { throw new Base64DataException("bad base-64"); } out.write(coder.output, 0, coder.op); } /** * If b.length is at least len, return b. Otherwise return a new byte array of length len. */ private byte[] embiggen(byte[] b, int len) { if (b == null || b.length < len) { return new byte[len]; } else { return b; } } }