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

卢声远*_* Lu 16 java 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();
        }
    }}
Run Code Online (Sandbox Code Playgroud)
  • 第一个问题:有没有更好的解决方案呢?
  • 第二个问题:我的另一个解决方案 - 在复制操作之前,我计算了这个InputStream的大小.所以我复制InputStream ByteArrayOutputStream然后得到size().但问题是InputStream可能没有markSupported(),因此InputStream不能在复制文件操作中重用.

idr*_*sid 18

我个人的选择是一个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.");
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 对于简单的文件上传情况:`if(IOUtils.copy(new BoundedInputStream(inputStream,MAX_SIZE + 1),new FileOutputStream(tmpFile))> MAX_SIZE){/ * error * /} (2认同)

hus*_*ayt 9

有以下现成解决方案:

  • ```uploadFile(ByteStreams.limit(stream,maxSize)); // TODO你怎么知道你是否达到maxSize限制?``` (3认同)

Ste*_*n C 3

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

并不真地。当然,并没有明显好转。

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

撇开以上是陈述而不是问题......

如果您已将字节复制到ByteArrayOutputStream,则可以ByteArrayInputStream从 所返回的字节数组创建一个baos.toByteArray()。所以你不需要标记/重置原始流。

然而,这是一种非常丑陋的实现方式。尤其是因为无论如何您都在读取和缓冲整个输入流。

  • 正确的。如果对该方法的大多数调用都导致中止,那么您可能会考虑将最多 1Mb 的数据读入缓冲区,并且仅在输入不太大时才创建输出文件。但我怀疑这是否属实。 (2认同)