复制InputStream,如果大小超过限制则中止操作

我尝试将InputStream复制到File,如果InputStream的大小大于1MB,则中止复制。 在Java7中,我编写了如下代码:

public void copy(InputStream input, Path target) { OutputStream out = Files.newOutputStream(target, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); boolean isExceed = false; try { long nread = 0L; byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = input.read(buf)) > 0) { out.write(buf, 0, n); nread += n; if (nread > 1024 * 1024) {// Exceed 1 MB isExceed = true; break; } } } catch (IOException ex) { throw ex; } finally { out.close(); if (isExceed) {// Abort the copy Files.deleteIfExists(target); throw new IllegalArgumentException(); } }} 
  • 第一个问题:有没有更好的解决方案?
  • 第二个问题:我的另一个解决方案 – 在复制操作之前,我计算这个InputStream的大小。 所以我将InputStream复制到ByteArrayOutputStream然后获取size() 。 但问题是InputStream可能没有markSupported() ,因此InputStream不能在复制文件操作中重用。

第一个问题:有没有更好的解决方案?

并不是的。 当然,并没有明显更好。

第二个问题:我的另一个解决方案 – 在复制操作之前,我计算InputStream的大小。 所以我将InputStream复制到ByteArrayOutputStream然后获取size()。 但问题是InputStream可能没有markSupported(),因此InputStream不能在复制文件操作中重用。

暂且不论以上是陈述不是问题……

如果已将字节复制到ByteArrayOutputStream ,则可以从baos.toByteArray()返回的字节数组中创建ByteArrayInputStream 。 因此,您无需标记/重置原始流。

但是,这是一种非常难看的实现方式。 尤其是因为您正在读取和缓冲整个输入流。

我个人的选择是一个InputStream包装器,它在读取字节时计算字节数:

 public class LimitedSizeInputStream extends InputStream { private final InputStream original; private final long maxSize; private long total; public LimitedSizeInputStream(InputStream original, long maxSize) { this.original = original; this.maxSize = maxSize; } @Override public int read() throws IOException { int i = original.read(); if (i>=0) incrementCounter(1); return i; } @Override public int read(byte b[]) throws IOException { return read(b, 0, b.length); } @Override public int read(byte b[], int off, int len) throws IOException { int i = original.read(b, off, len); if (i>=0) incrementCounter(i); return i; } private void incrementCounter(int size) throws IOException { total += size; if (total>maxSize) throw new IOException("InputStream exceeded maximum size in bytes."); } } 

我喜欢这种方法,因为它是透明的,它可以与所有输入流重复使用,并且可以很好地与其他库一起使用。 例如,使用Apache Commons复制最大4KB的文件:

 InputStream in = new LimitedSizeInputStream(new FileInputStream("from.txt"), 4096); OutputStream out = new FileOutputStream("to.txt"); IOUtils.copy(in, out); 

PS:上面的实现与BoundedInputStream的主要区别在于BoundedInputStream在超出限制时不抛出exception(它只是关闭流)

有以下现成的解决方案:

  • 来自Guava的ByteStreams.limit()
  • 来自Apache Commons的BoundedInputStream

我喜欢基于ByteArrayOutputStream的解决方案,我不明白为什么它无法工作

 public void copy(InputStream input, Path target) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); BufferedInputStream bis = new BufferedInputStream(input); for (int b = 0; (b = bis.read()) != -1;) { if (bos.size() > BUFFER_SIZE) { throw new IOException(); } bos.write(b); } Files.write(target, bos.toByteArray()); }