Java/File Input Output/FilterInputStream
Версия от 18:01, 31 мая 2010; (обсуждение)
A FilterInputStream with a limited bandwith
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/*********************************************
Copyright (c) 2001 by Daniel Matuschek
*********************************************/
/**
* A FilterInputStream with a limited bandwith
*
* This implements an filter for an existing input stream that allows
* it to limit the read bandwidth. This can be useful for network
* streams that should be limited to a specified bandwidth.
*
* @author
* @version $id$
*/
public class LimitedBandwidthStream
extends FilterInputStream {
/** usable bandwidth in bytes/second **/
private int bandwidth = 0;
/** bandwidth limit will be calculated form the start time **/
private boolean isReading = false;
/** number of bytes read **/
private int count = 0;
/** check bandwidth every n bytes **/
private static int CHECK_INTERVAL = 100;
/** start time **/
long starttime = 0;
/** used time **/
long usedtime = 0;
/**
* initializes the LimitedBandWidth stream
*/
public LimitedBandwidthStream (InputStream in, int bandwidth)
throws IOException
{
super(in);
if (bandwidth > 0) {
this.bandwidth=bandwidth;
} else {
this.bandwidth=0;
}
count = 0;
}
/**
* Reads the next byte.
*
* Reads the next byte of data from this input stream. The value byte
* is returned as an int in the range 0 to 255. If no byte is available
* because the end of the stream has been reached, the value -1 is
* returned. This method blocks until input data is available, the end
* of the stream is detected, or an exception is thrown.
* If the bandwidth consumption exceeds the defined limit, read will block
* until the bandwidth is in the limit again.
*
* @return the next byte from the stream or -1 if end-of-stream
*/
public int read()
throws IOException
{
long currentBandwidth;
if (! isReading) {
starttime = System.currentTimeMillis();
isReading = true;
}
// do bandwidth check only if bandwidth
if ((bandwidth > 0) &&
((count % CHECK_INTERVAL) == 0)) {
do {
usedtime = System.currentTimeMillis()-starttime;
if (usedtime > 0) {
currentBandwidth = (count*1000) / usedtime;
} else {
currentBandwidth = 0;
}
if (currentBandwidth > bandwidth) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
} while (currentBandwidth > bandwidth);
}
count++;
return super.read();
}
/**
* Shortcut for read(b,0,b.length)
*
* @see #read(byte[], int, int)
*/
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
/**
* Reads a block of bytes from the stream.
*
* If the bandwith is not limited, it simply used the
* read(byte[], int, int) method of the input stream, otherwise it
* uses multiple read() request to enforce bandwith limitation (this
* is easier to implement using byte reads).
*
* @return the number of bytes read or -1 at end of stream
*/
public int read(byte[] b, int off, int len) throws IOException {
int mycount = 0;
int current = 0;
// limit bandwidth ?
if (bandwidth > 0) {
for (int i=off; i < off+len; i++) {
current = read();
if (current == -1) {
return mycount;
} else {
b[i]=(byte)current;
count++;
mycount++;
}
}
return mycount;
} else {
return in.read(b, off, len);
}
}
} // LimitedBandwidthStream
introduce a protocol for reading arbitrary length data in a uniform way
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.DataInputStream;
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
/**
* This input stream works in conjunction with the WrappedOutputStream
* to introduce a protocol for reading arbitrary length data in a
* uniform way.
* <p>
* <strong>Note:</strong> See the javadoc for WrappedOutputStream for
* more information.
*
* @see WrappedOutputStream
*
* @author Andy Clark, IBM
*
* @version $Id: WrappedInputStream.java 447688 2006-09-19 02:39:49Z mrglavas $
*/
public class WrappedInputStream
extends FilterInputStream {
//
// Data
//
/** Bytes left on input stream for current packet. */
protected int fPacketCount;
/**
* Data input stream. This stream is used to input the block sizes
* from the data stream that are written by the WrappedOutputStream.
* <p>
* <strong>Note:</strong> The data input stream is only used for
* reading the byte count for performance reasons. We avoid the
* method indirection for reading the byte data.
*/
protected DataInputStream fDataInputStream;
/** To mark that the stream is "closed". */
protected boolean fClosed;
//
// Constructors
//
/** Constructs a wrapper for the given an input stream. */
public WrappedInputStream(InputStream stream) {
super(stream);
fDataInputStream = new DataInputStream(stream);
} // <init>(InputStream)
//
// InputStream methods
//
/** Reads a single byte. */
public int read() throws IOException {
// ignore, if already closed
if (fClosed) {
return -1;
}
// read packet header
if (fPacketCount == 0) {
fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
if (fPacketCount == 0) {
fClosed = true;
return -1;
}
}
// read a byte from the packet
fPacketCount--;
return super.in.read();
} // read():int
/**
* Reads a block of bytes and returns the total number of bytes read.
*/
public int read(byte[] b, int offset, int length) throws IOException {
// ignore, if already closed
if (fClosed) {
return -1;
}
// read packet header
if (fPacketCount == 0) {
fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
if (fPacketCount == 0) {
fClosed = true;
return -1;
}
}
// read bytes from packet
if (length > fPacketCount) {
length = fPacketCount;
}
int count = super.in.read(b, offset, length);
if (count == -1) {
// NOTE: This condition should not happen. The end of
// the stream should always be designated by a
// byte count header of 0. -Ac
fClosed = true;
return -1;
}
fPacketCount -= count;
// return total bytes read
return count;
} // read(byte[],int,int):int
/** Skips the specified number of bytes from the input stream. */
public long skip(long n) throws IOException {
if (!fClosed) {
// NOTE: This should be rewritten to be more efficient. -Ac
for (long i = 0; i < n; i++) {
int b = read();
if (b == -1) {
return i + 1;
}
}
return n;
}
return 0;
} // skip(long):long
/**
* Closes the input stream. This method will search for the end of
* the wrapped input, positioning the stream at after the end packet.
* <p>
* <strong>Note:</strong> This method does not close the underlying
* input stream.
*/
public void close() throws IOException {
if (!fClosed) {
fClosed = true;
do {
super.in.skip(fPacketCount);
fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
} while (fPacketCount > 0);
}
} // close()
} // class WrappedInputStream
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.DataOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* This output stream works in conjunction with the WrappedInputStream
* to introduce a protocol for sending arbitrary length data in a
* uniform way. This output stream allows variable length data to be
* inserted into an existing output stream so that it can be read by
* an input stream without reading too many bytes (in case of buffering
* by the input stream).
* <p>
* This output stream is used like any normal output stream. The protocol
* is introduced by the WrappedOutputStream and does not need to be known
* by the user of this class. However, for those that are interested, the
* method is described below.
* <p>
* The output stream writes the requested bytes as packets of binary
* information. The packet consists of a header and payload. The header
* is two bytes of a single unsigned short (written in network order)
* that specifies the length of bytes in the payload. A header value of
* 0 indicates that the stream is "closed".
* <p>
* <strong>Note:</strong> For this wrapped output stream to be used,
* the application <strong>must</strong> call <code>close()</code>
* to end the output.
*
* @see WrappedInputStream
*
* @author Andy Clark, IBM
*
* @version $Id: WrappedOutputStream.java 447688 2006-09-19 02:39:49Z mrglavas $
*/
public class WrappedOutputStream
extends FilterOutputStream {
//
// Constants
//
/** Default buffer size (1024). */
public static final int DEFAULT_BUFFER_SIZE = 1024;
//
// Data
//
/** Buffer. */
protected byte[] fBuffer;
/** Buffer position. */
protected int fPosition;
/**
* Data output stream. This stream is used to output the block sizes
* into the data stream that are read by the WrappedInputStream.
* <p>
* <strong>Note:</strong> The data output stream is only used for
* writing the byte count for performance reasons. We avoid the
* method indirection for writing the byte data.
*/
protected DataOutputStream fDataOutputStream;
//
// Constructors
//
/** Constructs a wrapper for the given output stream. */
public WrappedOutputStream(OutputStream stream) {
this(stream, DEFAULT_BUFFER_SIZE);
} // <init>(OutputStream)
/**
* Constructs a wrapper for the given output stream with the
* given buffer size.
*/
public WrappedOutputStream(OutputStream stream, int bufferSize) {
super(stream);
fBuffer = new byte[bufferSize];
fDataOutputStream = new DataOutputStream(stream);
} // <init>(OutputStream)
//
// OutputStream methods
//
/**
* Writes a single byte to the output.
* <p>
* <strong>Note:</strong> Single bytes written to the output stream
* will be buffered
*/
public void write(int b) throws IOException {
fBuffer[fPosition++] = (byte)b;
if (fPosition == fBuffer.length) {
fPosition = 0;
fDataOutputStream.writeInt(fBuffer.length);
super.out.write(fBuffer, 0, fBuffer.length);
}
} // write(int)
/** Writes an array of bytes to the output. */
public void write(byte[] b, int offset, int length)
throws IOException {
// flush existing buffer
if (fPosition > 0) {
flush0();
}
// write header followed by actual bytes
fDataOutputStream.writeInt(length);
super.out.write(b, offset, length);
} // write(byte[])
/**
* Flushes the output buffer, writing all bytes currently in
* the buffer to the output.
*/
public void flush() throws IOException {
flush0();
super.out.flush();
} // flush()
/**
* Closes the output stream. This method <strong>must</strong> be
* called when done writing all data to the output stream.
* <p>
* <strong>Note:</strong> This method does <em>not</em> close the
* actual output stream, only makes the input stream see the stream
* closed. Do not write bytes after closing the output stream.
*/
public void close() throws IOException {
flush0();
fDataOutputStream.writeInt(0);
super.out.flush();
} // close()
//
// Protected methods
//
/**
* Flushes the output buffer, writing all bytes currently in
* the buffer to the output. This method does not call the
* flush() method of the output stream; it merely writes the
* remaining bytes in the buffer.
*/
public void flush0() throws IOException {
int length = fPosition;
fPosition = 0;
if (length > 0) {
fDataOutputStream.writeInt(length);
super.out.write(fBuffer, 0, length);
}
} // flush0()
} // class WrappedOutputStream