com.hfg.util.io.FileBytes Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.util.io;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import com.hfg.exception.ProgrammingException;
import com.hfg.util.collection.CollectionUtil;
//------------------------------------------------------------------------------
/**
Container for a file name plus file contents as bytes.
Useful for holding temp files created in memory before producing a zip archive.
@author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
public class FileBytes extends OutputStream implements FileSource
{
private String mName;
private Long mLength;
private List mCompressedDataChunks;
private byte[] mUncompressedDataChunk;
private int mUncompressedDataChunkSize;
private static int sDefaultChunkSize = 8192;
//###########################################################################
// CONSTRUCTORS
//###########################################################################
//--------------------------------------------------------------------------
public FileBytes(String inName)
{
mName = inName;
}
//--------------------------------------------------------------------------
public FileBytes(File inFile)
throws IOException
{
mName = inFile.getName();
FileInputStream fileStream = null;
try
{
fileStream = new FileInputStream(inFile);
setData(StreamUtil.inputStreamToBytes(fileStream));
}
finally
{
StreamUtil.close(fileStream);
}
}
//###########################################################################
// PUBLIC METHODS
//###########################################################################
//--------------------------------------------------------------------------
@Deprecated
public String name()
{
return mName;
}
//--------------------------------------------------------------------------
@Override
public String getName()
{
return mName;
}
//--------------------------------------------------------------------------
public long length()
{
return mLength != null ? mLength : 0;
}
//--------------------------------------------------------------------------
/**
* This method directly sets the stored byte[] of content and bypasses any content compression.
* @param inData the specified byte[] of file contents
* @return this FileBytes object to enable method chaining.
* @throws IOException
*/
public FileBytes setData(byte[] inData)
throws IOException
{
clearData();
if (inData != null)
{
mUncompressedDataChunk = inData;
mUncompressedDataChunkSize = mUncompressedDataChunk.length;
mLength = (long) mUncompressedDataChunkSize;
}
return this;
}
//--------------------------------------------------------------------------
public FileBytes setData(InputStream inStream)
throws IOException
{
clearData();
if (inStream != null)
{
mCompressedDataChunks = new ArrayList<>(50);
BufferedInputStream bufferedStream = null;
try
{
bufferedStream = new BufferedInputStream(inStream);
byte[] readBuffer = new byte[4096];
int readSize = 0;
while ((readSize = bufferedStream.read(readBuffer)) >= 0)
{
mCompressedDataChunks.add(GZIP.compress(readBuffer, 0, readSize));
mLength += readSize;
}
}
finally
{
if (bufferedStream != null)
{
bufferedStream.close();
}
}
}
return this;
}
//--------------------------------------------------------------------------
public void write(int inByte)
throws IOException
{
appendData(inByte);
}
//--------------------------------------------------------------------------
public void write(byte[] inBytes)
throws IOException
{
appendData(inBytes);
}
//--------------------------------------------------------------------------
public void write(byte[] inBytes, int inOffset, int inLength)
throws IOException
{
appendData(inBytes, inOffset, inLength);
}
//--------------------------------------------------------------------------
public void appendData(int inByte)
throws IOException
{
if (null == mUncompressedDataChunk)
{
mUncompressedDataChunk = new byte[sDefaultChunkSize];
mUncompressedDataChunkSize = 0;
}
mUncompressedDataChunk[mUncompressedDataChunkSize++] = (byte) inByte;
if (mUncompressedDataChunkSize == mUncompressedDataChunk.length)
{
if (null == mCompressedDataChunks)
{
mCompressedDataChunks = new ArrayList<>(50);
}
mCompressedDataChunks.add(GZIP.compress(mUncompressedDataChunk, 0, mUncompressedDataChunkSize));
mUncompressedDataChunkSize = 0;
}
else
{
mUncompressedDataChunkSize++;
}
if (mLength != null)
{
mLength++;
}
else
{
mLength = 1L;
}
}
//--------------------------------------------------------------------------
public FileBytes appendData(byte[] inBytes)
throws IOException
{
return appendData(inBytes, 0, inBytes.length);
}
//--------------------------------------------------------------------------
public FileBytes appendData(byte[] inBytes, int inOffset, int inLength)
throws IOException
{
if (null == mUncompressedDataChunk)
{
mUncompressedDataChunk = new byte[sDefaultChunkSize];
mUncompressedDataChunkSize = 0;
}
int totalBytesAdded = 0;
while (totalBytesAdded < inLength)
{
int lengthRemainingToCopy = inLength - totalBytesAdded;
int bytesCopied = Math.min(lengthRemainingToCopy, mUncompressedDataChunk.length - mUncompressedDataChunkSize);
System.arraycopy(inBytes, inOffset, mUncompressedDataChunk, mUncompressedDataChunkSize, bytesCopied);
if (mUncompressedDataChunkSize == mUncompressedDataChunk.length)
{
if (null == mCompressedDataChunks)
{
mCompressedDataChunks = new ArrayList<>(50);
}
mCompressedDataChunks.add(GZIP.compress(mUncompressedDataChunk, 0, mUncompressedDataChunkSize));
mUncompressedDataChunkSize = 0;
}
else
{
mUncompressedDataChunkSize += bytesCopied;
}
totalBytesAdded += bytesCopied;
}
if (mLength != null)
{
mLength += inBytes.length;
}
else
{
mLength = (long) inLength;
}
return this;
}
//--------------------------------------------------------------------------
public FileBytes appendData(FileBytes inFileBytes)
throws IOException
{
if (inFileBytes.length() > 0)
{
if (null == mCompressedDataChunks)
{
mCompressedDataChunks = new ArrayList<>(50);
}
// Add whatever we have sitting uncompressed at the moment
if (mUncompressedDataChunkSize > 0)
{
mCompressedDataChunks.add(GZIP.compress(mUncompressedDataChunk, 0, mUncompressedDataChunkSize));
mUncompressedDataChunkSize = 0;
}
// Add the new compressed chunks
for (byte[] bytes : inFileBytes.mCompressedDataChunks)
{
mCompressedDataChunks.add(bytes);
}
// Add the new uncompressed chunk
if (inFileBytes.mUncompressedDataChunk != null)
{
mUncompressedDataChunk = Arrays.copyOf(inFileBytes.mUncompressedDataChunk, sDefaultChunkSize);
mUncompressedDataChunkSize = inFileBytes.mUncompressedDataChunkSize;
}
else
{
mUncompressedDataChunkSize = 0;
}
if (mLength != null)
{
mLength += inFileBytes.length();
}
else
{
mLength = inFileBytes.length();
}
}
return this;
}
//--------------------------------------------------------------------------
public byte[] getBytes()
{
ByteArrayOutputStream stream;
try
{
stream = new ByteArrayOutputStream();
StreamUtil.writeToStream(getDataStream(), stream);
}
catch (IOException e)
{
throw new ProgrammingException(e);
}
return stream.toByteArray();
}
//--------------------------------------------------------------------------
public InputStream getInputStream()
{
return new DataStreamer();
}
//--------------------------------------------------------------------------
@Deprecated
public InputStream getDataStream()
{
return new DataStreamer();
}
//--------------------------------------------------------------------------
public void writeData(OutputStream inStream)
throws IOException
{
if (CollectionUtil.hasValues(mCompressedDataChunks))
{
for (byte[] chunk : mCompressedDataChunks)
{
inStream.write(GZIP.uncompress(chunk));
}
}
if (mUncompressedDataChunkSize > 0)
{
inStream.write(mUncompressedDataChunk, 0, mUncompressedDataChunkSize);
}
inStream.flush();
}
//--------------------------------------------------------------------------
public static void streamAsZipFile(OutputStream inWriteTarget, String inBaseZipFileName, Collection inFiles)
throws IOException
{
ZipOutputStream zipStream = null;
try
{
zipStream = new ZipOutputStream(inWriteTarget);
for (FileBytes file : inFiles)
{
ZipEntry zipEntry = new ZipEntry(inBaseZipFileName + "/" + file.getName());
zipStream.putNextEntry(zipEntry);
file.writeData(zipStream);
zipStream.closeEntry();
}
}
finally
{
if (zipStream != null)
{
zipStream.finish();
}
}
}
//###########################################################################
// PRIVATE METHODS
//###########################################################################
//--------------------------------------------------------------------------
private void clearData()
{
mCompressedDataChunks = null;
mLength = 0L;
}
//###########################################################################
// INNER CLASS
//###########################################################################
private class DataStreamer extends InputStream
{
private byte[] mCurrentCompressedChunk;
private int mCurrentCompressedChunkIndex;
private int mUncompressedChunkIndex;
private int mByteIndex;
private boolean mDone = false;
int count = 0;
//-----------------------------------------------------------------------
public DataStreamer()
{
mCurrentCompressedChunkIndex = 0;
}
//-----------------------------------------------------------------------
public int read()
{
return (mDone ? -1 : getNextByte());
}
//-----------------------------------------------------------------------
@Override
public int read(byte b[], int off, int len)
throws IOException
{
if (b == null)
{
throw new NullPointerException();
}
else if (off < 0 || len < 0 || len > b.length - off)
{
throw new IndexOutOfBoundsException();
}
else if (len == 0)
{
return 0;
}
int c = read();
if (c == -1 && mDone)
{
return -1;
}
b[off] = (byte) c;
int i = 1;
for (; i < len; i++)
{
c = read();
if (c == -1 && mDone)
{
break;
}
b[off + i] = (byte) c;
}
return i;
}
//-----------------------------------------------------------------------
private byte getNextByte()
{
byte nextByte = -1;
if (null == mCurrentCompressedChunk
&& mCompressedDataChunks != null)
{
mCurrentCompressedChunk = GZIP.uncompress(mCompressedDataChunks.get(mCurrentCompressedChunkIndex));
mByteIndex = 0;
}
if (null == mCurrentCompressedChunk)
{
// Done with compressed chunks. Read from the uncompressed chunk.
nextByte = mUncompressedDataChunk[mUncompressedChunkIndex++];
if (mUncompressedChunkIndex >= mUncompressedDataChunkSize)
{
// This was the last chunk.
mDone = true;
}
}
else
{
nextByte = mCurrentCompressedChunk[mByteIndex++];
if (mByteIndex >= mCurrentCompressedChunk.length)
{
// This is the last byte in this chunk.
mCurrentCompressedChunk = null;
mCurrentCompressedChunkIndex++;
if (mCurrentCompressedChunkIndex < 0 || mCurrentCompressedChunkIndex == mCompressedDataChunks.size())
{
if (mUncompressedDataChunkSize > 0)
{
mUncompressedChunkIndex = 0;
}
else
{
// This was the last chunk.
mDone = true;
}
}
}
}
return nextByte;
}
}
}