import java.io.*;
import java.util.*;/**
* This class implements a FilterInputStream that reads base64-encoded
* data (as defined in RFC1521) from another InputStream and decodes
* it while reading.
*/
public class Base64InputStream extends FilterInputStream { private int surplus;
private int decoded;
private int lastRead = 0; /**
* The constructor for the decoding input stream.
*
* @param in the underlying input stream
*/
public Base64InputStream (InputStream in) {
super (in);
decoded = 0;
} /**
* This method always return 0, as we really can't be sure about the
* number of decoded bytes available without doing the decoding. Imaging
* an underlying input stream from which several whitespace characters
* can still be read. Thus calling available() on the underlying stream
* returns a number greater than zero. But whitespace decodes to nothing,
* so we can't derive a reliable number for available from he underlying
* stream.
*/
public int available () throws IOException {
return 0;
} /**
* Always returns false, since ing is not supported.
*/
public boolean Supported () {
return false;
} /**
* Reads a single byte. Returns -1 if the input stream is exhausted.
*/
public int read () throws IOException {
byte b[] = new byte[1];
int res = read (b);
if (res < 0)
return res;
return (b[0] & 0xff);
} /**
* Reads len bytes from the input stream, saving them in the array
* data beginning at index off.
* Returns -1 if the input stream is exhausted.
*
* @param data the array with bytes having been read.
* @param off the position of the first byte read within the array.
* @param len the number of bytes to be read.
*/
public int read (byte[] data, int off, int len) throws IOException {
int pos = off;
// EOF is EOF is EOF...
if (lastRead < 0)
return -1; // get from buf while not on boundary
while (len > 0 && decoded > 0 && lastRead >= 0) {
int b = decodeByte ();
if (b < 0) {
break;
}
data[pos++] = (byte)b;
len -= 1;
}
// shortcut while complete groups are requested
while (len >= 3 && lastRead >= 0) {
int grp0 = 0, grp1 = 0, grp2 = 0, grp3 = 0;
int gotCnt = 0;
while (gotCnt < 4) {
lastRead = in.read ();
if (lastRead < 0)
break;
if (lastRead == '=') {
// end of encoded data
if (gotCnt == 2) {
// a proper base64 sequence has a multiple of
// 4 characters, so another '=' should follow. Align.
in.read ();
}
lastRead = -1;
break;
}
int grp = codedBits((char)lastRead);
if (grp >= 0) {
switch (gotCnt++) {
case 0:
grp0 = grp;
break;
case 1:
grp1 = grp;
break;
case 2:
grp2 = grp;
break;
case 3:
grp3 = grp;
break;
}
}
}
switch (gotCnt) {
case 4:
data[pos + 2] = (byte)((grp2 << 6) | (grp3));
case 3:
data[pos + 1] = (byte)((grp1 << 4) | (grp2 >>> 2));
case 2:
data[pos] = (byte)((grp0 << 2) | (grp1 >>> 4));
pos += (gotCnt - 1);
len -= (gotCnt - 1);
}
}
// get rest of requested input
while (len > 0 && lastRead >= 0) {
int b = decodeByte ();
if (b < 0) {
break;
}
data[pos++] = (byte)b;
len -= 1;
}
if (len > 0 && pos == off)
return -1;
return pos - off;
} private int decodeByte () throws IOException {
int b;
switch (decoded) {
case 0:
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
lastRead = -1;
return -1;
}
} while ((b = codedBits ((char)lastRead)) < 0);
b = (b << 2);
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
lastRead = -1;
return -1;
}
} while ((surplus = codedBits ((char)lastRead)) < 0);
b = b | (surplus >>> 4);
surplus = (surplus & 0xf);
decoded = 1;
return b; case 1:
b = (surplus << 4);
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
if (lastRead == '=') {
// a proper base64 sequence has a multiple of
// 4 characters, so another '=' should follow. Align.
in.read ();
}
lastRead = -1;
return -1;
}
} while ((surplus = codedBits ((char)lastRead)) < 0);
b = b | (surplus >>> 2);
surplus = (surplus & 0x3);
decoded = 2;
return b; case 2:
b = (surplus << 6);
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
lastRead = -1;
return -1;
}
} while ((surplus = codedBits ((char)lastRead)) < 0);
b = b | surplus;
decoded = 0;
return b;
}
// never realastReaded, but compiler can't know and complains
return -1;
} private int codedBits (char c) {
if ('A' <= c && c <= 'Z')
return c - 'A';
if ('a' <= c && c <= 'z')
return c - 'a' + 26;
if ('0' <= c && c <= '9')
return c - '0' + 52;
if (c == '+')
return 62;
if (c == '/')
return 63;
return -1;
}
}
import java.util.*;/**
* This class implements a FilterInputStream that reads base64-encoded
* data (as defined in RFC1521) from another InputStream and decodes
* it while reading.
*/
public class Base64InputStream extends FilterInputStream { private int surplus;
private int decoded;
private int lastRead = 0; /**
* The constructor for the decoding input stream.
*
* @param in the underlying input stream
*/
public Base64InputStream (InputStream in) {
super (in);
decoded = 0;
} /**
* This method always return 0, as we really can't be sure about the
* number of decoded bytes available without doing the decoding. Imaging
* an underlying input stream from which several whitespace characters
* can still be read. Thus calling available() on the underlying stream
* returns a number greater than zero. But whitespace decodes to nothing,
* so we can't derive a reliable number for available from he underlying
* stream.
*/
public int available () throws IOException {
return 0;
} /**
* Always returns false, since ing is not supported.
*/
public boolean Supported () {
return false;
} /**
* Reads a single byte. Returns -1 if the input stream is exhausted.
*/
public int read () throws IOException {
byte b[] = new byte[1];
int res = read (b);
if (res < 0)
return res;
return (b[0] & 0xff);
} /**
* Reads len bytes from the input stream, saving them in the array
* data beginning at index off.
* Returns -1 if the input stream is exhausted.
*
* @param data the array with bytes having been read.
* @param off the position of the first byte read within the array.
* @param len the number of bytes to be read.
*/
public int read (byte[] data, int off, int len) throws IOException {
int pos = off;
// EOF is EOF is EOF...
if (lastRead < 0)
return -1; // get from buf while not on boundary
while (len > 0 && decoded > 0 && lastRead >= 0) {
int b = decodeByte ();
if (b < 0) {
break;
}
data[pos++] = (byte)b;
len -= 1;
}
// shortcut while complete groups are requested
while (len >= 3 && lastRead >= 0) {
int grp0 = 0, grp1 = 0, grp2 = 0, grp3 = 0;
int gotCnt = 0;
while (gotCnt < 4) {
lastRead = in.read ();
if (lastRead < 0)
break;
if (lastRead == '=') {
// end of encoded data
if (gotCnt == 2) {
// a proper base64 sequence has a multiple of
// 4 characters, so another '=' should follow. Align.
in.read ();
}
lastRead = -1;
break;
}
int grp = codedBits((char)lastRead);
if (grp >= 0) {
switch (gotCnt++) {
case 0:
grp0 = grp;
break;
case 1:
grp1 = grp;
break;
case 2:
grp2 = grp;
break;
case 3:
grp3 = grp;
break;
}
}
}
switch (gotCnt) {
case 4:
data[pos + 2] = (byte)((grp2 << 6) | (grp3));
case 3:
data[pos + 1] = (byte)((grp1 << 4) | (grp2 >>> 2));
case 2:
data[pos] = (byte)((grp0 << 2) | (grp1 >>> 4));
pos += (gotCnt - 1);
len -= (gotCnt - 1);
}
}
// get rest of requested input
while (len > 0 && lastRead >= 0) {
int b = decodeByte ();
if (b < 0) {
break;
}
data[pos++] = (byte)b;
len -= 1;
}
if (len > 0 && pos == off)
return -1;
return pos - off;
} private int decodeByte () throws IOException {
int b;
switch (decoded) {
case 0:
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
lastRead = -1;
return -1;
}
} while ((b = codedBits ((char)lastRead)) < 0);
b = (b << 2);
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
lastRead = -1;
return -1;
}
} while ((surplus = codedBits ((char)lastRead)) < 0);
b = b | (surplus >>> 4);
surplus = (surplus & 0xf);
decoded = 1;
return b; case 1:
b = (surplus << 4);
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
if (lastRead == '=') {
// a proper base64 sequence has a multiple of
// 4 characters, so another '=' should follow. Align.
in.read ();
}
lastRead = -1;
return -1;
}
} while ((surplus = codedBits ((char)lastRead)) < 0);
b = b | (surplus >>> 2);
surplus = (surplus & 0x3);
decoded = 2;
return b; case 2:
b = (surplus << 6);
do {
lastRead = in.read ();
if (lastRead < 0 || lastRead == '=') {
lastRead = -1;
return -1;
}
} while ((surplus = codedBits ((char)lastRead)) < 0);
b = b | surplus;
decoded = 0;
return b;
}
// never realastReaded, but compiler can't know and complains
return -1;
} private int codedBits (char c) {
if ('A' <= c && c <= 'Z')
return c - 'A';
if ('a' <= c && c <= 'z')
return c - 'a' + 26;
if ('0' <= c && c <= '9')
return c - '0' + 52;
if (c == '+')
return 62;
if (c == '/')
return 63;
return -1;
}
}
import java.io.*;
import java.util.*;/**
* This class implements a FilterOutputStream that writes base64-encoded
* data (as defined in RFC1521) to an underlying OutputStream, encoding the
* data while it is being written.
*
* Note that this implementation does not attempt to do any canonalization.
* I.e. if used to encode text in an RFC1521 conformant way, care must be
* taken to convert line breaks
* to CRLF before passing the data to the Base64OutputStream. The lack of
* canonalization is considered a feature as it allows the
* Base64OutputStream to be used for binary data. (Maybe one day I'll add
* a TextCanonalizationOutputStream.)
*/
public class Base64OutputStream extends FilterOutputStream { private final static char charCodes[] = {
'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','+','/'
}; private byte encBuf[] = new byte[4];
private int encoded;
private int surplus;
private int col;
private boolean finishOnFlush; /**
* The constructor for the encoding output stream.
*
* @param out the underlying output stream.
* @param finishOnFlush controls the behaviour of the flush method.
*
* @see org.tsx.mnl.io.base64.Base64OutputStream#flush
*/
public Base64OutputStream (OutputStream out, boolean finishOnFlush) {
super (out);
encoded = 0;
col = 0;
this.finishOnFlush = finishOnFlush;
} /**
* Sets the value of finishOnFlush.
*
* @param value the new value.
*
* @see org.tsx.mnl.io.base64.Base64OutputStream#flush
*/
public void setFinishOnFlush (boolean value) {
finishOnFlush = value;
} /**
* Encodes and writes a single byte to the output stream.
*
* @param b the byte to be written.
*/
public void write (int b) throws IOException {
byte a[] = { (byte)(b & 0xff) };
write (a, 0, 1);
} /**
* Encodes and writes len bytes from array data to the outputstream,
* starting with the byte at position off.
*
* @param data the array holding the bytes to be written.
* @param off the position off the first byte to be written within the
* array.
* @param len the number of bytes to be written.
*/
public void write (byte[] data, int off, int len) throws IOException {
int pos = off;
// fill encBuf if possible
while (encoded > 0 && pos < off + len) {
encodeByte (data[pos]);
pos += 1;
}
// encode triples for efficiency
while (pos + 2 < off + len) {
byte b0 = data[pos];
byte b1 = data[pos + 1];
byte b2 = data[pos + 2];
encBuf[0] = (byte)charCodes[((b0 >> 2) & 0x3f)];
encBuf[1] = (byte)charCodes[((b0 & 0x3) << 4)|((b1 >> 4) & 0xf)];
encBuf[2] = (byte)charCodes[((b1 & 0xf) << 2)|((b2 >> 6) & 0x3)];
encBuf[3] = (byte)charCodes[b2 & 0x3f];
out.write (encBuf);
encoded = 0;
col += 4;
if (col >= 76) {
out.write ('\n');
col = 0;
}
pos += 3;
}
// encode rest
while (pos < off + len) {
encodeByte (data[pos]);
pos += 1;
}
} private void encodeByte (byte b) throws IOException {
switch (encoded) {
case 0:
encBuf[0] = (byte)charCodes[(b >> 2) & 0x3f];
surplus = ((b & 0x3) << 4);
encBuf[1] = (byte)charCodes[surplus];
encoded += 1;
break;
case 1:
encBuf[1] = (byte)charCodes[surplus | ((b >> 4) & 0xf)];
surplus = ((b & 0xf) << 2);
encBuf[2] = (byte)charCodes[surplus];
encoded += 1;
break;
case 2:
encBuf[2] = (byte)charCodes[surplus | ((b >>> 6) & 0x3)];
encBuf[3] = (byte)charCodes[b & 0x3f];
out.write (encBuf);
encoded = 0;
col += 4;
if (col >= 76) {
out.write ('\n');
col = 0;
}
break;
}
} /**
* This method is to write all buffered data to its destination.
* With a base64 encoding there arises a problem: if an encoding character
* isn't completly known yet because its value depends partially
* on the last byte written and partially on the next byte to be
* written, then flush can't write it without assuming that there
* will be no more bytes coming.
*
* The behaviour for flush can therefore
* be controlled by the flag finishOnFlush when the stream is created.
* If the flag is set, flush will assume that no more data is to be
* written after flush has been called, thus effectivly finishing the
* stream of encoded data. This behaviour is especially useful, when
* the Base64OutputStream is created with an underlying output stream
* that contains mixed
* content. In this case, we cannot call close on the Base64OutputStream
* because this would make the underlying OutputStream unusable as well.
*
* @see org.tsx.mnl.io.base64.Base64OutputStream
* @see java.io.OutputStream#flush
*/
public void flush () throws IOException {
if (finishOnFlush)
doFlush ();
} private void doFlush () throws IOException {
switch (encoded) {
case 1:
encBuf[2] = (byte)'=';
case 2:
encBuf[3] = (byte)'=';
out.write (encBuf);
col += 4;
break;
}
if (col > 0) {
out.write ('\n');
col = 0;
}
} /**
* This method finishes the encoded stream and calls close on the
* underlying output stream.
*/
public void close () throws IOException {
doFlush ();
out.close ();
}
}
import java.io.*;class Test { public static void main(String[] args) { try {
Base64OutputStream enc
= new Base64OutputStream (System.out, false);
OutputStreamWriter out = new OutputStreamWriter (enc);
String s = "Hello world!\n\n";
s = s + "While normally the short message above is sufficient\n";
s = s + "for a test, we need more text in order to get another\n";
s = s + "line of output.\n";
out.write (s, 0, s.length());
enc.setFinishOnFlush (true);
out.flush (); FileInputStream data = new FileInputStream ("data.base64");
InputStream dec = new Base64InputStream (data);
System.out.print ((char)dec.read ());
byte[] b = new byte[16];
int cnt;
while ((cnt = dec.read (b)) >= 0)
System.out.print (new String (b, 0, cnt));
System.out.print ("Besides the base64 data, input contains: ");
while ((cnt = data.read ()) >= 0)
System.out.print ((char)cnt);
} catch (Exception e) {
System.out.println ("Error: " + e.getMessage());
e.printStackTrace();
}
}
}