OutOfMemoryException while decrypting file using facebook conceal - java

I'm working on android application where I need to save Videos in SD Card which must not be transferable that's why I'm encrypting and decrypting as and when needed with Facebook Conceal which works perfectly fine if video size is smaller.
Whenever I tries to do encryption and decryption to large video files not more than 10MB in GenyMotion running 2.3.7 it crashes with OutOfMemoryException which means I'm running out of heap memory allocated to my application which can't be handled but must be prevented.
Tried :
Apache Common Utils IO package
Various IO Utils
Facebook Conceal : Says while decrypting
You must read the entire stream to completion.
The verification is done at the end of the stream.
Thus not reading till the end of the stream will cause
a security bug. For safety, you should not
use any of the data until it's been fully read or throw
away the data if an exception occurs.
Code I'm invoking which encryption and decryption with Facebook Conceal :
Encryption :
public void startEncryption() {
// Creates a new Crypto object with default implementations of
// a key chain as well as native library.
// Check for whether the crypto functionality is available
// This might fail if android does not load libaries correctly.
if (!crypto.isAvailable()) {
return;
}
OutputStream fileStream;
try {
File mEncryptedFile = new File(mPlainFile.getPath().substring(0,
mPlainFile.getPath().length() - 4)
+ "_encrypted"
+ mPlainFile.getPath().substring(
mPlainFile.getPath().length() - 4,
mPlainFile.getPath().length()));
fileStream = new BufferedOutputStream(new FileOutputStream(
mEncryptedFile));
// Creates an output stream which encrypts the data as
// it is written to it and writes it out to the file.
OutputStream outputStream;
outputStream = crypto.getCipherOutputStream(fileStream, entity);
outputStream.write(FileUtils.readFileToByteArray(mPlainFile));
// fileStream.flush();
// fileStream.close();
// outputStream.flush();
// outputStream.close();
// outputStream.flush();
File mRenameTo = new File(mPlainFile.getPath());
mPlainFile.delete();
mEncryptedFile.renameTo(mRenameTo);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CryptoInitializationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeyChainException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Decryption :
public String startDecryption() {
// Get the file to which ciphertext has been written.
try {
FileInputStream fileStream = new FileInputStream(mPlainFile);
// Creates an input stream which decrypts the data as
// it is read from it.
InputStream inputStream;
inputStream = crypto.getCipherInputStream(fileStream, entity);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// org.apache.commons.io.output.ByteArrayOutputStream out = new
// org.apache.commons.io.output.ByteArrayOutputStream(1024);
// Read into a byte array.
// int read;
// byte[] buffer = new byte[1024];
// // You must read the entire stream to completion.
// // The verification is done at the end of the stream.
// // Thus not reading till the end of the stream will cause
// // a security bug.
// int i = 0;
// while ((read = inputStream.read(buffer)) != -1) {
// out.write(buffer, 0, read);
// Log.i(TAG, "bytearrayoutputstream "+i++ + " "+read + " " +
// buffer.length + " "+out.size());
// }
mDecryptedFile = new File(mPlainFile.getPath().substring(0,
mPlainFile.getPath().length() - 4)
+ "_decrypted"
+ (mPlainFile.getPath().substring(mPlainFile.getPath()
.length() - 4, mPlainFile.getPath().length())));
OutputStream outputStream = new FileOutputStream(mDecryptedFile);
// IOUtils.copy(inputStream, outputStream);
try {
final byte[] buffer = new byte[1024];
int read;
while ((read = inputStream.read(buffer)) != -1)
outputStream.write(buffer, 0, read);
outputStream.flush();
} catch (Exception e) {
} finally {
outputStream.close();
}
// out.writeTo(outputStream);
// out.flush();
// out.close();
// fileStream.close();
inputStream.close();
// outputStream.flush();
outputStream.close();
return mDecryptedFile.getPath();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CryptoInitializationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeyChainException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Is there any solution which can work around and encrypts and decrypts large video files too?

Looks like you are reading in the whole file into a byte array. What you should do is create an input stream and feed it to the crypto engine.
So this:
outputStream.write(FileUtils.readFileToByteArray(mPlainFile));
Should look like your decryption loop.
BufferedInputStream bis = new BufferedInputStream(mPlainFile);
InputStream inputStream = crypto.getCipherInputStream(
bis,
entity);
while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
Not sure if this is what you ended up with. I pulled the crypto decoding from the docs. I think this confirms that their intent is to say the stream must be read until the end.

As Bruce did highlight on bottleneck on Encryption which leads to OutOfMemoryException at time of Decryption. So here's code which i'm executing while encrypting and decrypting which no more leads to OutOfMemoryException.
Encryption :
fileStream = new BufferedOutputStream(new FileOutputStream(mEncryptedFile));
OutputStream outputStream;
outputStream = crypto.getCipherOutputStream(fileStream, entity);
int read;
byte[] buffer = new byte[1024];
BufferedInputStream bis = new BufferedInputStream(newFileInputStream(mPlainFile));
while ((read = bis.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
outputStream.close();
bis.close();
Decryption :
InputStream inputStream;
inputStream = crypto.getCipherInputStream(fileStream, entity);
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStream outputStream = new FileOutputStream(mDecryptedFile);
BufferedInputStream bis = new BufferedInputStream(inputStream);
int mRead;
byte[] mBuffer = new byte[1024];
while ((mRead = bis.read(mBuffer)) != -1) {
outputStream.write(mBuffer, 0, mRead);
}
bis.close();
out.writeTo(outputStream);
inputStream.close();
outputStream.close();
out.close();

Related

Unable to create zip file from InputStream

I have a requirement to create a zip file from input stream data, and before writing to zip I need to find the checksum for the input stream.
To do that I am using below codes:
private String writeZipFileToFS(List<ResponsePacks> attachmentList) throws IOException
{
File fileToWrite = new File(getZipPath() + "fileName.zip");
try
{
FileUtils.copyInputStreamToFile(compress(attachmentList), fileToWrite);
}
catch (IOException e)
{
throw e;
}
return fileName;
}
private InputStream compress(List<ResponsePacks> attachmentList)
{
byte buffer[] = new byte[2048];
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ZipOutputStream zipFileToSend = new ZipOutputStream(byteStream);
try
{
for (ResponsePacks info : attachmentList)
{
// only for successful requests files would need to be added
zipFileToSend.putNextEntry(new ZipEntry(info.getFileName()));
InputStream in = info.getFileContentStream();
getCheckSum(in, info.getFileName());
int length;
while ((length = in.read(buffer)) >= 0)
{
zipFileToSend.write(buffer, 0, length);
}
zipFileToSend.closeEntry();
}
zipFileToSend.close();
}
catch (IOException e)
{
throw e;
}
return new ByteArrayInputStream(byteStream.toByteArray());
}
private static void getCheckSum(InputStream is, String fileName)
{
byte[] dataCopy = null;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try
{
IOUtils.copy(is, outputStream);
dataCopy = outputStream.toByteArray();
printLog("Byte Array Size {}", dataCopy.length);
String checkSum = calculateChecksum(dataCopy);
printLog("Checksum for file {} {}", fileName, checkSum);
outputStream.flush();
outputStream.close();
}
catch (Exception e)
{
printLog("Error on calculationg checksum {}", e.getMessage());
}
}
private static String calculateChecksum(byte[] dataCopy)
{
try (ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(dataCopy)))
{
ZipEntry zipEntry;
MessageDigest digest = DigestUtils.getSha256Digest();
DWriter writer = new DWriter(digest);
while ((zipEntry = zipInputStream.getNextEntry()) != null)
{
byte[] entityData = IOUtils.toByteArray(zipInputStream);
if (!zipEntry.isDirectory())
{
writer.write(entityData);
}
}
if (writer.getChecksum() != null)
{
return writer.getChecksum();
}
}
catch (Exception e)
{
throw e;
}
return "";
}
static class DWriter
{
private final MessageDigest myDigest;
DWriter(MessageDigest digest)
{
myDigest = digest;
}
public void write(byte[] data)
{
myDigest.update(data);
}
public String getChecksum()
{
return new String(Hex.encodeHex(myDigest.digest()));
}
}
But problem is if I am adding code to calculate the checksum then zip file creating with empty content and if I am removing the checksum calculation code then zip file creating with proper contents.
And also when I check the log I found InputStream contents different contents but still I am getting the same checkSum (empty string) always as below:
Byte Array Size 20854
Checksum for file 20200910173919142.json e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Byte Array Size 14383
Checksum for file 1599752440405.zip e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
I am unable to find where I am doing wrong, due to which zip file is creating with empty content, and checkSum also creating same always.
Requesting to help me to find where I am doing wrong.
You consume twice the same inputstream: first you read it to get the checksum and the you read it again to write the zip entry.
getCheckSum(in, info.getFileName());
int length;
while ((length = in.read(buffer)) >= 0)
{
zipFileToSend.write(buffer, 0, length);
}
The second time you're trying to read, there's nothing to read anymore, so nothing gets written into the zip entry.
Some input streams can be reset and read multiple times, if that's not the case here you would need to save the data into a ByteArrayOutputStream (as you're already doing inside the getCheckSum() method), and then you could read that data multiple times.

The server is sending data through the socket, but the client is not receving it (Java)

I am writing a file storage and transfer system using Java. Here's the code on the client side to receive a file:
public static void receiveFile(Socket socket) throws IOException{
String fileLocation="/home/limafoxtrottango/Downloads/receivedFile";
int bytesRead=0;
int current = 0;
FileOutputStream fileOutputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
// receive file
byte [] byteArray = new byte [60022386];
System.out.println("Waiting to receive a file...");
//reading file from socket
InputStream inputStream = socket.getInputStream();
fileOutputStream = new FileOutputStream(fileLocation);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
bytesRead = inputStream.read(byteArray,0,byteArray.length); //copying file from socket to byteArray
current = bytesRead;
do {
bytesRead =inputStream.read(byteArray, current, (byteArray.length-current));
if(bytesRead >= 0) current += bytesRead;
} while(bytesRead > -1);
bufferedOutputStream.write(byteArray, 0 , current); //writing byteArray to file
bufferedOutputStream.flush(); //flushing buffers
System.out.println("File " + fileLocation + " downloaded ( size: " + current + " bytes read)");
} catch(SocketException e){
System.out.println("Some error occured");
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
if (fileOutputStream != null) fileOutputStream.close();
if (bufferedOutputStream != null) bufferedOutputStream.close();
if (socket != null) socket.close();
}
}
While receiving a file, I get the following error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.io.BufferedOutputStream.write(BufferedOutputStream.java:128)
at Test.receiveFile(Test.java:211)
at Test.main(Test.java:70)
Note: The error is in the following line of the code:
bufferedOutputStream.write(byteArray, 0 , current);
After debugging, I found-out that the client does not have any data in it's input stream, and hence, the read() method always returns -1 (eof). But the server is sending the file successfully.
Here is the code for the server:
public static void sendFile(Socket socket, String fileLocation)
{
FileInputStream fileInputStream = null;
BufferedInputStream bufferedInputStream = null;
OutputStream outputStream = null;
File file = new File (fileLocation);
byte [] byteArray = new byte [(int)file.length()];
try {
socket=new Socket(socket.getInetAddress(),port_no);
fileInputStream = new FileInputStream(file);
bufferedInputStream = new BufferedInputStream(fileInputStream);
bufferedInputStream.read(byteArray,0,byteArray.length); // copied file into byteArray
//sending file through socket
outputStream = socket.getOutputStream();
System.out.println("Sending " + fileLocation + "( size: " + byteArray.length + " bytes)");
outputStream.write(byteArray,0,byteArray.length); //copying byteArray to socket
outputStream.flush(); //flushing socket
System.out.println("Done sending!");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
And here is my call to the above method:
sendFile(clientSocket, "/home/limafoxtrottango/Downloads/serverDownloads/"+sender);
The thing is that the server is successfully writing the byte into the stream, but the client doesn't seem to have any data in it's input stream.
https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#read(byte[],%20int,%20int)
inputStream.read(byteArray,0,byteArray.length); may return -1 in some cases as given in documentation above. Please cater for such situations.
In addition, I would suggest to use solution given here for both client and server: Efficient way to write InputStream to a File in Java (Version 6)
Client code:
final Path destination = Paths.get(fileLocation);
try (
final InputStream in = socket.getInputStream();
) {
Files.copy(in, destination);
}
Server code:
try (
final InputStream in = new FileInputStream(fileLocation);
) {
Files.copy(in, socket.getOutputStream());
}
Kind regards,
Bala
The server isn't sending anything, contrary to your title. It is closing the connection immediately, so bytesRead is initially -1 and never changes, and you aren't defending against that, so you get the ArrayIndexOutOfBoundsException.
However in the code you posted, the server is sending something but never closing the socket, which is another bug you need to fix. It is also ignoring the count returned by FileInputStream.read() and assuming it filled the buffer, which isn't part of the specification.
So either this is not the real server code or you are connecting to something else, or the server got an IOException that you haven't mentioned.
It's curious that you use two different pieces of code for copying. The standard way to copy a stream in Java is this:
char buffer = new char[8192]; // or whatever size you prefer > 0
int count;
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Use this at both ends. There is no need for heroically sized buffers, or buffers the size of the file, or assuming that the file size fits into an int.

Save Vaadin7 StreamResource to file

I have an object of StreamResource class w/ some needed content in it. I'm sure this content is valid and i need to save it locally for the further processing. Here is the code snippet:
OutputStream os = new FileOutputStream(filePath, false);
byte[] buffer = new byte[1024];
int bytesRead;
//read from is to buffer
try {
while(true)
{
bytesRead = resource.getStream().getStream().read(buffer);
if(bytesRead == -1)
break;
os.write(buffer, 0, bytesRead);
resource.getStream().getStream().skip(bytesRead);
}
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
And here i join the endless loop. Break is never ocured and file in the needed location becomes huge as it should actually. Under debugger i see that read() operation returns only the 1st chunk of bytes in each iteration even w/ skip() call after os.write.
How should i read the content from the stream?
Thanks.
According to the source code of StreamResource a new DownloadStream is created on each call. You should call it only once like in the following snippet:
OutputStream os = new FileOutputStream(filePath, false);
byte[] buffer = new byte[1024];
int bytesRead;
//read from is to buffer
try {
DownloadStream stream = resource.getStream();
while(true)
{
bytesRead = stream.getStream().read(buffer);
if(bytesRead == -1)
break;
os.write(buffer, 0, bytesRead);
}
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
That way the stream will not be read from the beginning on every iteration. However, I still don't get why you use the indirection over StreamResource because I guess you create that object before.

Streaming encrypted audio bytes over socket

I have updated the question after the comments to make the problem clearer.I am trying to write a code for encrypted voice call over TCP. Of course SSL is one of the options when it comes to a secure connection, but just now I'm working on TCP, like any other project.
The program aims to capture the audio coming from microphone, encrypt it using AES and then send the data to the server.On the server side the received data would be decrypted and sent to the speaker. But with these codes I get a LineUnavailable Exception from the client side during runtime:
Unable to open the line: javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 16000.0 Hz, 8 bit, stereo, 2 bytes/frame, not supported.
Normally, without encryption there is no problem with the codes, while I use a BufferedOutputStream(s.getOutputStream()) object for the recorded data; and the sound is transmitted over network successfully, i.e my hardware supports the mentioned PCM format.
On the other hand, in a simple standalone capture/play java module I have tested the encryption codes, where I have managed to encrypt and decrypt the audio data between the capturing and saving processes, while it was a byte[] data, just before it was saved to an output wav file. There seems no problem with the encryption process itself.
The problem arises during networking, when I use a ByteArrayOutputStream to write the audio data coming from DataLine, instead of using a BufferedOutputStream object which was directly taking the s.getOutputStream() method before. The reason to use a ByteArrayOutputStream here is to pass the captured bytes as an input argument to the encrypting method. It is worth to note that the ByteArrayOutputStream works well, while saving audio bytes to a wav file on local disk when there is no sockets around.
The question for me now is the disintegration between a working OutputStream object and the bytes waiting for encryption, considering a TCP network. My final test was the unsuccessful one with DataOutput/InputStream objects in the code below. Still need any ideas for an appropriate Input/Output streaming method to achieve a successful communication, if any.
The code piece for capturing and sending data is:
public void run() {
try {
dos = new DataOutputStream(s.getOutputStream());// need the exact stream obj.
} catch (IOException ex) {
return;
}
AudioFormat format =new AudioFormat(16000,8,2,true,true);
DataLine.Info info = new DataLine.Info(TargetDataLine.class,format);
if (!AudioSystem.isLineSupported(info)) {
System.err.println("Line matching " + info + " not supported.");// throws the exception
return;
}
try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format, line.getBufferSize());
} catch (LineUnavailableException ex) {
System.err.println("Unable to open the line: " + ex);// related to exception
return;
}
byte[] data = new byte[256];
int numBytesRead=0;
line.start();
// In the nonsecure call version, the audio data is written directly
// to the BufferedOutputStream(s.getOutputStream()) and transmitted without problem
ByteArrayOutputStream caps = new ByteArrayOutputStream(); //?
while (thread != null) {
numBytesRead = line.read(data, 0,128);
try {
caps.write(data, 0, numBytesRead);
} catch (Exception ex) {
System.err.println("Unable to read/write line data: " + ex);
break;
}
}
line.stop();
line.close();
line = null;
try {
caps.flush();
caps.close();
} catch (IOException ex) { ex.printStackTrace(); }
try {
SecretKeySpec skeySpec = CryptUtil.getSecretKeySpec("password12345678","AES",128);
byte[] encrypted = CryptUtil.encrypt(caps.toByteArray(), skeySpec);
dos.writeInt(encrypted.length);
dos.write(encrypted,0,encrypted.length);
} catch (Exception ex) { }
}
The code piece for receiving and playing data is:
public void run() {
AudioFormat format =new AudioFormat(16000,8,2,true,true);
try {
dis = new DataInputStream(s.getInputStream());// need the exact stream obj.
} catch (Exception e) {
System.err.println("Could not get InputStream: " + e);
}
try {
int length = dis.readInt();
byte[] message = new byte[length];
dis.readFully(message);
SecretKeySpec skeySpec = CryptUtil.getSecretKeySpec("password12345678","AES",128);
byte[] audioBytes = CryptUtil.decrypt(message, skeySpec);
ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
playStream = new AudioInputStream(bais, format, audioBytes.length / format.getFrameSize());
} catch (Exception e) {
System.err.println("Could not decrypt the stream: " + e);
}
DataLine.Info info = new DataLine.Info(SourceDataLine.class,format);
try {
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format, bufSize);
} catch (LineUnavailableException ex) {
System.err.println("Unable to open the line: " + ex);
return;
}
byte[] data = new byte[256];
int numBytesRead = 0;
line.start();
while (thread != null) {
try{
numBytesRead = playStream.read(data);
line.write(data, 0,numBytesRead);
} catch (Exception e) {
System.err.println("Error during playback: " + e);
break;
}
}
if (thread != null) {
line.drain();
}
line.stop();
line.close();
line = null;
}
Best to try and use a simpler approach for leveraging streaming encryption in your code using Encryptor4j: https://github.com/martinwithaar/Encryptor4j
Wrap an OutputStream for encryption:
Encryptor encryptor = new Encryptor(secretKey, "AES/CTR/NoPadding", 16);
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("original.jpg");
os = encryptor.wrapOutputStream(new FileOutputStream("encrypted.jpg"));
byte[] buffer = new byte[4096];
int nRead;
while((nRead = is.read(buffer)) != -1) {
os.write(buffer, 0, nRead);
}
os.flush();
} finally {
if(is != null) {
is.close();
}
if(os != null) {
os.close();
}
}
And an InputStream for decryption:
Encryptor encryptor = new Encryptor(secretKey, "AES/CTR/NoPadding", 16);
InputStream is = null;
OutputStream os = null;
try {
is = encryptor.wrapInputStream(new FileInputStream("encrypted.jpg"));
os = new FileOutputStream("decrypted.jpg");
byte[] buffer = new byte[4096];
int nRead;
while((nRead = is.read(buffer)) != -1) {
os.write(buffer, 0, nRead);
}
os.flush();
} finally {
if(is != null) {
is.close();
}
if(os != null) {
os.close();
}
}

OutOfMemory when creating Base64 string in java?

I used ostermillerutils library to create base64 string but I get OutOfMemory error if the image is heavy. If the image I try to convert is a simple image, the code is working fine.
public String createBase64String(InputStream in) {
//collect = new ByteArrayOutputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
try {
for(int readNum; (readNum = in.read(buf)) != -1; ) {
bos.write(buf, 0, readNum);
}
}
catch (IOException ex) {
Logger.getInstance().debug("XML createBase64String: IOException");
return null;
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
;
}
}
}
byte[] ba = bos.toByteArray();
String coded = Base64.encodeToString(ba);
return coded;
}
I also tried doing this but the base64 was incorrect when I tried to decode it.
public void createBase64String(InputStream in) throws IOException {
//collect = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int readNum = 0;
try {
while((readNum = in.read(buf)) != -1)
{
smtp.addBase64(Base64.encodeBase64String(buf));
}
}
catch (IOException ex) {
Logger.getInstance().debug("XML createBase64String: IOException");
}
finally {
if (in != null) {
in.close();
}
}
}
Please suggest solutions for JDK 1.4 and also for later versions of Java.
If you like to write the encoded content straight into a file then use the following code
public void encode(File file, OutputStream base64OutputStream) {
InputStream is = new FileInputStream(file);
OutputStream out = new Base64OutputStream(base64OutputStream)
IOUtils.copy(is, out);
is.close();
out.close();
}
IOUtils class from Apache Commons IO.
EDIT
Since you want to do it using BufferedWriter, use it as follows
OutputStream out = Base64OutputStream(smtpSocket.getOutputStream());
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
IOUtils.copy(is, bw);
It sounds like the problem is that you're having to manipulate too much data in memory when you read the entire image. One fix would be to increase the Java heap size until you have enough memory, but that would just be avoiding the problem rather than solving it.
A better option would be to look at a streaming implementation of a Base64 encoder. This would mean you're only working on a subset of the image at any time. I believe that Base64OutputStream from Apache Commons would do the job for you.
I've fixed my problem by using javabase64-1.3.1.jar library.
OutputStream fos2 = FileUtil.getOutputStream(base64FileName, FileUtil.HDD);
InputStream in2 = FileUtil.getInputStream(fileName, FileUtil.HDD);
Base64.encode(in2, fos2);
in2.close();
fos2.close();
I stored the base64 string to a text file first.
public void createBase64String(InputStream in) throws IOException {
baos = new ByteArrayOutputStream();
byte[] buf = new byte[BUFFER_SIZE];
int readNum = 0;
smtp.addBase64("\t\t");
try {
while ((readNum = in.read(buf)) >= 0) {
baos.write(buf, 0, readNum);
smtp.addBase64(baos.toString());
baos.reset();
}
}
catch (IOException ex) {
LogUtil.error("Sending of Base64 String to SMTP: IOException: " + ex);
}
finally {
if (in != null) {
in.close();
baos.close();
}
}
baos = null;
buf = null;
}
then send each line to smtp's socket outputstream.
From Java 8 onwards, there is a simple way to implement base64 encoding in an output stream with one line of code and no external dependencies:
import java.util.Base64;
OutputStream os = ...
OutputStream base64 = Base64.getEncoder().wrap(os);
Base64 also provides other flavors of base64 encoder; see javadocs:
Base64
Base64.Encoder.wrap

Categories

Resources