写入具有容量限制的 OutputStream
writing to OutputStream having capacity restriction
根据我之前询问的 :我正在实施具有容量限制的 ByteArrayOutputStream
。我的主要限制是可用内存量。所以有这样的流 os
:
当我向输出流写入更多 1MB
时,我需要 "stop"。
我宁愿不抛出异常,而是写 os
的完整内容
输出流到指定的其他输出流参数。
OutputStream out;
os.writeTo(out);
然后继续写到os
从头开始
为了防止出现1.描述的情况,我更倾向于drain os
,
和 possible 一样频繁。我的意思是将数据从它复制到 out
in chuncks
共 512KB
可行吗?如果是,有什么建议怎么办?或者可能有一个内置的 class 可以满足我的要求
编辑: 写入 out
的字节数也有限制。我最多可以在那里写1GB。如果我有更多,我需要创建其他输出流以便从 os
那里排出。
写入os的过程。可以是那样的。那里写了 500MB - 我立即将它转移到外面。几秒钟后,那里写入了 700MB - 我只需要将 500MB 排出到 out
,将其他 200MB 排出到其他输出流(out2
),我需要在这种情况下创建这些输出流
您所描述的是一个 BufferedOutputStream,您可以这样构造它:
new BufferedOutputStream(out, 512000)
第一个参数是您拥有的另一个输出流,第二个参数是 BufferedOutputStream 内部缓冲区的大小
编辑:
好的,一开始我没有完全理解你的需求。您确实需要扩展 OutputStream 才能实现这一目标。这是一个示例代码:
下面是如何使用下面的代码:
public static void main(String[] args) throws IOException {
AtomicLong idx = new AtomicLong(0);
try (
OutputStream out = new OutputStreamMultiVolume(10, () -> new FileOutputStream(getNextFilename(idx)));
) {
out.write("01234567890123456789012345678901234567890123456789".getBytes("UTF-8"));
}
}
private static File getNextFilename(AtomicLong idx) {
return new File("sample.file." + idx.incrementAndGet() + ".txt");
}
OutputStreamMultiVolume 的第一个构造函数 arg 是卷的最大大小。如果我们达到这个大小,我们将关闭当前的 outputStream,并调用 OutputStreamSupplier 获取下一个。
此处的示例代码会将字符串 01234567890123456789012345678901234567890123456789
(0123456789 的 5 倍)写入名为 'sample.file.idx.txt' 的文件,其中每次达到外流最大大小时 idx 都会增加(因此您将得到 5文件)。
和 class 本身:
public class OutputStreamMultiVolume extends OutputStream {
private final long maxBytePerVolume;
private long bytesInCurrentVolume = 0;
private OutputStream out;
private OutputStreamSupplier outputStreamSupplier;
static interface OutputStreamSupplier {
OutputStream get() throws IOException;
}
public OutputStreamMultiVolume(long maxBytePerOutput, OutputStreamSupplier outputStreamSupplier) throws IOException {
this.outputStreamSupplier = outputStreamSupplier;
this.maxBytePerVolume = maxBytePerOutput;
this.out = outputStreamSupplier.get();
}
@Override
public synchronized void write(byte[] bytes) throws IOException {
final int remainingBytesInVol = (int) (maxBytePerVolume - bytesInCurrentVolume);
if (remainingBytesInVol >= bytes.length) {
out.write(bytes);
bytesInCurrentVolume += bytes.length;
return;
}
out.write(bytes, 0, remainingBytesInVol);
switchOutput();
this.write(bytes, remainingBytesInVol, bytes.length - remainingBytesInVol);
}
@Override
public synchronized void write(int b) throws IOException {
if (bytesInCurrentVolume + 1 <= maxBytePerVolume) {
out.write(b);
bytesInCurrentVolume += 1;
return;
}
switchOutput();
out.write(b);
bytesInCurrentVolume += 1;
}
@Override
public synchronized void write(byte[] b, int off, int len) throws IOException {
final int remainingBytesInVol = (int) (maxBytePerVolume - bytesInCurrentVolume);
if (remainingBytesInVol >= len) {
out.write(b, off, len);
bytesInCurrentVolume += len;
return;
}
out.write(b, off, remainingBytesInVol);
switchOutput();
this.write(b, off + remainingBytesInVol, len - remainingBytesInVol);
bytesInCurrentVolume += len - remainingBytesInVol;
}
private void switchOutput() throws IOException {
out.flush();
out.close();
out = outputStreamSupplier.get();
bytesInCurrentVolume = 0;
}
@Override
public synchronized void close() throws IOException {
out.close();
}
@Override
public synchronized void flush() throws IOException {
out.flush();
}
}
我想您可以尝试将 java.nio.ByteBuffer
与具有方法 newChannel(OutputStream);
的 java.nio.channel.Channels
结合使用
像这样:
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
//... use buffer
OutputStream out = ...
drainBuffer(buffer, out);
和
public void drainBuffer(ByteBuffer buffer, OutputStream stream) {
WritableByteChannel channel = Channels.newChannel(stream);
channel.write(buffer);
}
恐怕您原来的 没有完全解释,所以您得到的答案也不是。
你不应该使用也不扩展BytArrayOutputStream来刷新,因为它的主要特点是“将数据写入字节数组”:即:所有数据都在内存中,所以您可以稍后通过 toByteArray
.
检索它
如果你想刷新你超出的数据,你需要一个缓冲方法:这个结构就足够了:
OutputStream out=new FileOutputStream(...);
out=new BufferedOutputStream(out, 1024*1024);
为了定期刷新数据,您可以安排一个 TimerTask 来调用 flush
:
Timer timer=new Timer(true);
TimerTask timerTask=new TimerTask(){
public void run()
{
try
{
out.flush();
}
catch (IOException e)
{
...
}
};
timer.schedule(timerTask, delay, period);
根据我之前询问的 ByteArrayOutputStream
。我的主要限制是可用内存量。所以有这样的流 os
:
当我向输出流写入更多
1MB
时,我需要 "stop"。 我宁愿不抛出异常,而是写os
的完整内容 输出流到指定的其他输出流参数。OutputStream out; os.writeTo(out);
然后继续写到os
从头开始为了防止出现1.描述的情况,我更倾向于drain
os
, 和 possible 一样频繁。我的意思是将数据从它复制到out
in chuncks 共 512KB 可行吗?如果是,有什么建议怎么办?或者可能有一个内置的 class 可以满足我的要求
编辑: 写入 out
的字节数也有限制。我最多可以在那里写1GB。如果我有更多,我需要创建其他输出流以便从 os
那里排出。
写入os的过程。可以是那样的。那里写了 500MB - 我立即将它转移到外面。几秒钟后,那里写入了 700MB - 我只需要将 500MB 排出到 out
,将其他 200MB 排出到其他输出流(out2
),我需要在这种情况下创建这些输出流
您所描述的是一个 BufferedOutputStream,您可以这样构造它:
new BufferedOutputStream(out, 512000)
第一个参数是您拥有的另一个输出流,第二个参数是 BufferedOutputStream 内部缓冲区的大小
编辑:
好的,一开始我没有完全理解你的需求。您确实需要扩展 OutputStream 才能实现这一目标。这是一个示例代码:
下面是如何使用下面的代码:
public static void main(String[] args) throws IOException {
AtomicLong idx = new AtomicLong(0);
try (
OutputStream out = new OutputStreamMultiVolume(10, () -> new FileOutputStream(getNextFilename(idx)));
) {
out.write("01234567890123456789012345678901234567890123456789".getBytes("UTF-8"));
}
}
private static File getNextFilename(AtomicLong idx) {
return new File("sample.file." + idx.incrementAndGet() + ".txt");
}
OutputStreamMultiVolume 的第一个构造函数 arg 是卷的最大大小。如果我们达到这个大小,我们将关闭当前的 outputStream,并调用 OutputStreamSupplier 获取下一个。
此处的示例代码会将字符串 01234567890123456789012345678901234567890123456789
(0123456789 的 5 倍)写入名为 'sample.file.idx.txt' 的文件,其中每次达到外流最大大小时 idx 都会增加(因此您将得到 5文件)。
和 class 本身:
public class OutputStreamMultiVolume extends OutputStream {
private final long maxBytePerVolume;
private long bytesInCurrentVolume = 0;
private OutputStream out;
private OutputStreamSupplier outputStreamSupplier;
static interface OutputStreamSupplier {
OutputStream get() throws IOException;
}
public OutputStreamMultiVolume(long maxBytePerOutput, OutputStreamSupplier outputStreamSupplier) throws IOException {
this.outputStreamSupplier = outputStreamSupplier;
this.maxBytePerVolume = maxBytePerOutput;
this.out = outputStreamSupplier.get();
}
@Override
public synchronized void write(byte[] bytes) throws IOException {
final int remainingBytesInVol = (int) (maxBytePerVolume - bytesInCurrentVolume);
if (remainingBytesInVol >= bytes.length) {
out.write(bytes);
bytesInCurrentVolume += bytes.length;
return;
}
out.write(bytes, 0, remainingBytesInVol);
switchOutput();
this.write(bytes, remainingBytesInVol, bytes.length - remainingBytesInVol);
}
@Override
public synchronized void write(int b) throws IOException {
if (bytesInCurrentVolume + 1 <= maxBytePerVolume) {
out.write(b);
bytesInCurrentVolume += 1;
return;
}
switchOutput();
out.write(b);
bytesInCurrentVolume += 1;
}
@Override
public synchronized void write(byte[] b, int off, int len) throws IOException {
final int remainingBytesInVol = (int) (maxBytePerVolume - bytesInCurrentVolume);
if (remainingBytesInVol >= len) {
out.write(b, off, len);
bytesInCurrentVolume += len;
return;
}
out.write(b, off, remainingBytesInVol);
switchOutput();
this.write(b, off + remainingBytesInVol, len - remainingBytesInVol);
bytesInCurrentVolume += len - remainingBytesInVol;
}
private void switchOutput() throws IOException {
out.flush();
out.close();
out = outputStreamSupplier.get();
bytesInCurrentVolume = 0;
}
@Override
public synchronized void close() throws IOException {
out.close();
}
@Override
public synchronized void flush() throws IOException {
out.flush();
}
}
我想您可以尝试将 java.nio.ByteBuffer
与具有方法 newChannel(OutputStream);
java.nio.channel.Channels
结合使用
像这样:
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
//... use buffer
OutputStream out = ...
drainBuffer(buffer, out);
和
public void drainBuffer(ByteBuffer buffer, OutputStream stream) {
WritableByteChannel channel = Channels.newChannel(stream);
channel.write(buffer);
}
恐怕您原来的
你不应该使用也不扩展BytArrayOutputStream来刷新,因为它的主要特点是“将数据写入字节数组”:即:所有数据都在内存中,所以您可以稍后通过 toByteArray
.
如果你想刷新你超出的数据,你需要一个缓冲方法:这个结构就足够了:
OutputStream out=new FileOutputStream(...);
out=new BufferedOutputStream(out, 1024*1024);
为了定期刷新数据,您可以安排一个 TimerTask 来调用 flush
:
Timer timer=new Timer(true);
TimerTask timerTask=new TimerTask(){
public void run()
{
try
{
out.flush();
}
catch (IOException e)
{
...
}
};
timer.schedule(timerTask, delay, period);