2013-03-16 3 views
9

Я попытался скопировать InputStream в файл и прервать копию, если размер InputStream превышает 1 МБ. В Java7, я написал код, как показано ниже:Копировать InputStream, прервать операцию, если размер превышает предел

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 не может быть повторно использован в операции копирования файлов.
+0

Я бы сделал тест перед записью, а не после него. Вы не можете вычислить размер 'InputStream'. Это может быть бесконечно. Понятие не имеет смысла. Воспроизведение трюков с помощью 'mark()' и 'reset()' может работать только при загрузке всего потока, чего именно вы пытаетесь избежать. – EJP

ответ

2

Первый вопрос: есть ли лучшее решение для этого?

Не совсем. Конечно, не намного лучше.

Второй вопрос: другое решение - перед операцией копирования я вычисляю размер InputStream. Поэтому я копирую InputStream в ByteArrayOutputStream, затем получаю size(). Но проблема заключается в том, что InputStream может не отмечатьSupported(), поэтому InputStream не может быть повторно использован в операции копирования файлов.

Оставляя в стороне, что выше это утверждение не вопрос ...

Если вы скопировали байты в ByteArrayOutputStream, вы можете создать ByteArrayInputStream из массива байтов, возвращаемого baos.toByteArray(). Поэтому вам не нужно отмечать/перезагружать исходный поток.

Однако, это довольно уродливый способ реализации этого. Не в последнюю очередь потому, что вы читаете и буферируете весь поток ввода в любом случае.

+0

Спасибо! Вы имеете в виду, что согласны с моим решением 'copy()'? –

+1

Исправить. Если MOST вызовов метода привел к прерываниям, вы можете рассмотреть возможность чтения до 1 Мб в буфер и только создание выходного файла, если вход не слишком велик. Но я сомневаюсь, что это будет правдой. –

0

Мне нравится 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()); 
} 
+0

Обязательно, это * может * произведение. Я просто задал вопрос * утилите * для буферизации файла в памяти. Если мы не предположим, что значительная часть «слишком большого» случая будет значительным, дешевле и проще просто написать прямо в выходной файл ... и удалить его в случае ошибки, как это делает OP. –

+0

Голосовать. Меня беспокоит то, что в будущем предел будет намного выше, так как ByteArrayOutputStream будет потреблять память hugh. –

+0

Зачем тратить всю эту память? – EJP

5

Там Подписчики готовых решений для этого:

+1

'' 'uploadFile (ByteStreams.limit (поток, maxSize)); // TODO Как бы вы определили, достигли ли вы предела maxSize? '' ' – RvPr

9

Мой личный выбор является 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."); 
    } 

} 

Мне нравится этот подход, поскольку он является прозрачным, она пригодна для повторного использования со всеми входными потоками, и он хорошо работает с другими библиотеками.Например копирование файлов до 4 Кбайт с Apache Commons:

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

PS: Основное отличие реализации выше с BoundedInputStream что BoundedInputStream не выбрасывает исключение при превышении лимита (она просто закрывает поток)

Смежные вопросы