复制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()); }
- java.lang.NullPointerException:尝试在空对象引用上调用虚方法’boolean java.lang.String.equals(java.lang.Object)’
- Eclipse生成的equals():getOuterType()?