I'm attempting to access Gmail using IMAP. The problem is that when I read a Base64 encoded bodypart, the content stream is truncated.
The problem is the same whether I call getInputStream() on the content (the resulting binary is too short and the Base64DecodingStream complains about bad input) or call getRawInputStream() and examine the encoded bytes - it's short and I get an EOF before the end of the real attachment. It occurs about 2k before the end of the actual attachment for the attachments I've tested, which include 16k, 150k, and 400k attachments. It occurs for all gmail accounts that I've tested (3), and across a number of mime types (msword, pdf, and .zip).
When I save the attachments directly from Gmail, the resulting binaries are fine.
I set mail.imap.partialfetch to false, but that didn't help.
Any pointers would be greatly appreciated. I've pasted a bit of code below.
protected InputStream getAttachmentStream(MimeMultipart parent, MessageAttachment localAttachment)
throws MessagingException, IOException {
// localAttachment is an (currently) unpopulated model of the attachment that I want to populate with data.
InputStream is = null;
for(int i = 0; i < parent.getCount(); i++) {
MimeBodyPart part = (MimeBodyPart) parent.getBodyPart(i);
String partType = part.getContentType();
if(partType.toLowerCase().startsWith("multipart")) {
is = getAttachmentStream((MimeMultipart)part.getContent(), localAttachment);
if(is != null) break;
}
else {
String filename = part.getFileName();
int size = part.getSize();
if( filename != null &&
size == localAttachment.getAttachmentSize() &&
filename.equals(localAttachment.getAttachmentName())) {
part.saveFile("/Users/Rod/test.doc"); // this results in a truncated file
is = part.getRawInputStream();
// test code: read the input stream fully and examine the bytes
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
byte [] buffer = new byte[1024];
int bytesRead = 0;
do {
try {
bytesRead = is.read(buffer);
totalbytes += bytesRead;
if (bytesRead < 0) {
break; // while
}
bytestream.write(buffer, 0, bytesRead);
}
catch(Exception ex) {
ex.printStackTrace();
bytesRead = 0;
}
} while(bytesRead > 0);
logger.log(Level.INFO, "Loaded attachment with " + totalbytes + " bytes"); // is about 2k shorter than it should be
byte[] rawdata = bytestream.toByteArray();
logger.log(Level.INFO, "Raw data is " + rawdata.length + " bytes"); // matches totalbytes - just a sanity check
/*
Output the encoded data. I generally get something like ends like this:
/zcBAAA4AQAAOQEAADoBAAA7AQAAPAEAAD0BAAD+////PwEAAEABAABBAQAAQgEAAEMBAABEAQAA
RQEAAP7////9/////f////3///9KAQAA/v////7////+////////////////////////////////
////////////////////////////////////////////////////////
in this case, were it valid base64 there would be two '=' characters at the end to pad out to a multiple of 3.
*/
logger.log(Level.INFO, new String(rawdata));
// Amazon's decoder doesn't choke as soon as the Base64DecoderStream, but I only get a few more
// '/' characters decoded
byte[] data = com.amazon.thirdparty.Base64.decode(rawdata, 0, rawdata.length);
// length of data is about 1.5k less than the binary saved directly from Gmail
logger.log(Level.INFO, "Decoded attachment size: " + data.length + " bytes");
localAttachment.setMimeType(partType);
break;
}
}
}
return is;
}