commons-io之ThresholdingOutputStream 和 DeferredFileOutputStream

[size=medium]
1. ThresholdingOutputStream
这个类的意图主要是可以处理设置了临界值的OutputStream做出处理,
它当然是复写了三个write方法。
有三个步骤:
1. 检测是否到达临界值
2. 调用底层的Stream写数据
3. 改变已写数据的游标written
里面有两个抽象的方法:
protected abstract OutputStream getStream()
protected abstract void thresholdReached()
意图分别是得到底层的Stream,一个是可以让子类在threshold到达后可以做出响应。
[/size]
[size=medium]
2. DeferredFileOutputStream
是ThresholdingOutputStream的子类
它的意图也很明确,主要是实现延迟写。
首先是数据会写到基于内存的OutStream, 当到达threshold后,会将内存中的数据又
写到指定的文件里面。这个类里面比较有亮点的是thresholdReached函数的实现,
首先将memoryOutputStream里面的数据写到目标文件的FileOutputStream里面,
将memoryOutputStream设置成null,然后将currentOutputStream更新为目标文件的
FileOutputStream,这样后续的数据就会写入文件中。

[/size]

[img]http://dl.iteye.com/upload/attachment/269284/eab0fc2d-bdf7-3a3a-89c1-a34043525b7b.png[/img]

package org.apache.commons.io.output;

import java.io.IOException;
import java.io.OutputStream;


/**
* An output stream which triggers an event when a specified number of bytes of
* data have been written to it. The event can be used, for example, to throw
* an exception if a maximum has been reached, or to switch the underlying
* stream type when the threshold is exceeded.
* <p>
* This class overrides all <code>OutputStream</code> methods. However, these
* overrides ultimately call the corresponding methods in the underlying output
* stream implementation.
* <p>
* NOTE: This implementation may trigger the event <em>before</em> the threshold
* is actually reached, since it triggers when a pending write operation would
* cause the threshold to be exceeded.
*
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
*
* @version $Id: ThresholdingOutputStream.java 540714 2007-05-22 19:39:44Z niallp $
*/
public abstract class ThresholdingOutputStream
extends OutputStream
{

// ----------------------------------------------------------- Data members


/**
* The threshold at which the event will be triggered.
*/
private int threshold;


/**
* The number of bytes written to the output stream.
*/
private long written;


/**
* Whether or not the configured threshold has been exceeded.
*/
private boolean thresholdExceeded;


// ----------------------------------------------------------- Constructors


/**
* Constructs an instance of this class which will trigger an event at the
* specified threshold.
*
* @param threshold The number of bytes at which to trigger an event.
*/
public ThresholdingOutputStream(int threshold)
{
this.threshold = threshold;
}


// --------------------------------------------------- OutputStream methods


/**
* Writes the specified byte to this output stream.
*
* @param b The byte to be written.
*
* @exception IOException if an error occurs.
*/
public void write(int b) throws IOException
{
checkThreshold(1);
getStream().write(b);
written++;
}


/**
* Writes <code>b.length</code> bytes from the specified byte array to this
* output stream.
*
* @param b The array of bytes to be written.
*
* @exception IOException if an error occurs.
*/
public void write(byte b[]) throws IOException
{
checkThreshold(b.length);
getStream().write(b);
written += b.length;
}


/**
* Writes <code>len</code> bytes from the specified byte array starting at
* offset <code>off</code> to this output stream.
*
* @param b The byte array from which the data will be written.
* @param off The start offset in the byte array.
* @param len The number of bytes to write.
*
* @exception IOException if an error occurs.
*/
public void write(byte b[], int off, int len) throws IOException
{
checkThreshold(len);
getStream().write(b, off, len);
written += len;
}


/**
* Flushes this output stream and forces any buffered output bytes to be
* written out.
*
* @exception IOException if an error occurs.
*/
public void flush() throws IOException
{
getStream().flush();
}


/**
* Closes this output stream and releases any system resources associated
* with this stream.
*
* @exception IOException if an error occurs.
*/
public void close() throws IOException
{
try
{
flush();
}
catch (IOException ignored)
{
// ignore
}
getStream().close();
}


// --------------------------------------------------------- Public methods


/**
* Returns the threshold, in bytes, at which an event will be triggered.
*
* @return The threshold point, in bytes.
*/
public int getThreshold()
{
return threshold;
}


/**
* Returns the number of bytes that have been written to this output stream.
*
* @return The number of bytes written.
*/
public long getByteCount()
{
return written;
}


/**
* Determines whether or not the configured threshold has been exceeded for
* this output stream.
*
* @return <code>true</code> if the threshold has been reached;
* <code>false</code> otherwise.
*/
public boolean isThresholdExceeded()
{
return (written > threshold);
}


// ------------------------------------------------------ Protected methods


/**
* Checks to see if writing the specified number of bytes would cause the
* configured threshold to be exceeded. If so, triggers an event to allow
* a concrete implementation to take action on this.
*
* @param count The number of bytes about to be written to the underlying
* output stream.
*
* @exception IOException if an error occurs.
*/
protected void checkThreshold(int count) throws IOException
{
if (!thresholdExceeded && (written + count > threshold))
{
thresholdExceeded = true;
thresholdReached();
}
}

/**
* Resets the byteCount to zero. You can call this from
* {@link #thresholdReached()} if you want the event to be triggered again.
*/
protected void resetByteCount()
{
this.thresholdExceeded = false;
this.written = 0;
}

// ------------------------------------------------------- Abstract methods


/**
* Returns the underlying output stream, to which the corresponding
* <code>OutputStream</code> methods in this class will ultimately delegate.
*
* @return The underlying output stream.
*
* @exception IOException if an error occurs.
*/
protected abstract OutputStream getStream() throws IOException;


/**
* Indicates that the configured threshold has been reached, and that a
* subclass should take whatever action necessary on this event. This may
* include changing the underlying output stream.
*
* @exception IOException if an error occurs.
*/
protected abstract void thresholdReached() throws IOException;
}



package org.apache.commons.io.output;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.commons.io.IOUtils;


/**
* An output stream which will retain data in memory until a specified
* threshold is reached, and only then commit it to disk. If the stream is
* closed before the threshold is reached, the data will not be written to
* disk at all.
* <p>
* This class originated in FileUpload processing. In this use case, you do
* not know in advance the size of the file being uploaded. If the file is small
* you want to store it in memory (for speed), but if the file is large you want
* to store it to file (to avoid memory issues).
*
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
* @author gaxzerow
*
* @version $Id: DeferredFileOutputStream.java 606381 2007-12-22 02:03:16Z ggregory $
*/
public class DeferredFileOutputStream
extends ThresholdingOutputStream
{

// ----------------------------------------------------------- Data members


/**
* The output stream to which data will be written prior to the theshold
* being reached.
*/
private ByteArrayOutputStream memoryOutputStream;


/**
* The output stream to which data will be written at any given time. This
* will always be one of <code>memoryOutputStream</code> or
* <code>diskOutputStream</code>.
*/
private OutputStream currentOutputStream;


/**
* The file to which output will be directed if the threshold is exceeded.
*/
private File outputFile;

/**
* The temporary file prefix.
*/
private String prefix;

/**
* The temporary file suffix.
*/
private String suffix;

/**
* The directory to use for temporary files.
*/
private File directory;


/**
* True when close() has been called successfully.
*/
private boolean closed = false;

// ----------------------------------------------------------- Constructors


/**
* Constructs an instance of this class which will trigger an event at the
* specified threshold, and save data to a file beyond that point.
*
* @param threshold The number of bytes at which to trigger an event.
* @param outputFile The file to which data is saved beyond the threshold.
*/
public DeferredFileOutputStream(int threshold, File outputFile)
{
super(threshold);
this.outputFile = outputFile;

memoryOutputStream = new ByteArrayOutputStream();
currentOutputStream = memoryOutputStream;
}


/**
* Constructs an instance of this class which will trigger an event at the
* specified threshold, and save data to a temporary file beyond that point.
*
* @param threshold The number of bytes at which to trigger an event.
* @param prefix Prefix to use for the temporary file.
* @param suffix Suffix to use for the temporary file.
* @param directory Temporary file directory.
*
* @since Commons IO 1.4
*/
public DeferredFileOutputStream(int threshold, String prefix, String suffix, File directory)
{
this(threshold, (File)null);
if (prefix == null) {
throw new IllegalArgumentException("Temporary file prefix is missing");
}
this.prefix = prefix;
this.suffix = suffix;
this.directory = directory;
}


// --------------------------------------- ThresholdingOutputStream methods


/**
* Returns the current output stream. This may be memory based or disk
* based, depending on the current state with respect to the threshold.
*
* @return The underlying output stream.
*
* @exception IOException if an error occurs.
*/
protected OutputStream getStream() throws IOException
{
return currentOutputStream;
}


/**
* Switches the underlying output stream from a memory based stream to one
* that is backed by disk. This is the point at which we realise that too
* much data is being written to keep in memory, so we elect to switch to
* disk-based storage.
*
* @exception IOException if an error occurs.
*/
protected void thresholdReached() throws IOException
{
if (prefix != null) {
outputFile = File.createTempFile(prefix, suffix, directory);
}
FileOutputStream fos = new FileOutputStream(outputFile);
memoryOutputStream.writeTo(fos);
currentOutputStream = fos;
memoryOutputStream = null;
}


// --------------------------------------------------------- Public methods


/**
* Determines whether or not the data for this output stream has been
* retained in memory.
*
* @return <code>true</code> if the data is available in memory;
* <code>false</code> otherwise.
*/
public boolean isInMemory()
{
return (!isThresholdExceeded());
}


/**
* Returns the data for this output stream as an array of bytes, assuming
* that the data has been retained in memory. If the data was written to
* disk, this method returns <code>null</code>.
*
* @return The data for this output stream, or <code>null</code> if no such
* data is available.
*/
public byte[] getData()
{
if (memoryOutputStream != null)
{
return memoryOutputStream.toByteArray();
}
return null;
}


/**
* Returns either the output file specified in the constructor or
* the temporary file created or null.
* <p>
* If the constructor specifying the file is used then it returns that
* same output file, even when threashold has not been reached.
* <p>
* If constructor specifying a temporary file prefix/suffix is used
* then the temporary file created once the threashold is reached is returned
* If the threshold was not reached then <code>null</code> is returned.
*
* @return The file for this output stream, or <code>null</code> if no such
* file exists.
*/
public File getFile()
{
return outputFile;
}


/**
* Closes underlying output stream, and mark this as closed
*
* @exception IOException if an error occurs.
*/
public void close() throws IOException
{
super.close();
closed = true;
}


/**
* Writes the data from this output stream to the specified output stream,
* after it has been closed.
*
* @param out output stream to write to.
* @exception IOException if this stream is not yet closed or an error occurs.
*/
public void writeTo(OutputStream out) throws IOException
{
// we may only need to check if this is closed if we are working with a file
// but we should force the habit of closing wether we are working with
// a file or memory.
if (!closed)
{
throw new IOException("Stream not closed");
}

if(isInMemory())
{
memoryOutputStream.writeTo(out);
}
else
{
FileInputStream fis = new FileInputStream(outputFile);
try {
IOUtils.copy(fis, out);
} finally {
IOUtils.closeQuietly(fis);
}
}
}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值