Простой способ записать содержимое Java InputStream в OutputStream - PullRequest
404 голосов
/ 04 сентября 2008

Сегодня я с удивлением обнаружил, что не могу отыскать простой способ записать содержимое InputStream в OutputStream в Java. Очевидно, что код байтового буфера не сложно написать, но я подозреваю, что мне просто не хватает чего-то, что сделало бы мою жизнь проще (и код понятнее).

Итак, при InputStream in и OutputStream out существует ли более простой способ написать следующее?

byte[] buffer = new byte[1024];
int len = in.read(buffer);
while (len != -1) {
    out.write(buffer, 0, len);
    len = in.read(buffer);
}

Ответы [ 21 ]

382 голосов
/ 09 сентября 2008

Как упоминалось в WMR, org.apache.commons.io.IOUtils от Apache имеет метод с именем copy(InputStream,OutputStream), который делает именно то, что вы ищете.

Итак, у вас есть:

InputStream in;
OutputStream out;
IOUtils.copy(in,out);
in.close();
out.close();

... в вашем коде.

Есть ли причина, по которой вы избегаете IOUtils?

313 голосов
/ 05 октября 2013

Если вы используете Java 7, Файлы (в стандартной библиотеке) - лучший подход:

/* You can get Path from file also: file.toPath() */
Files.copy(InputStream in, Path target)
Files.copy(Path source, OutputStream out)

Редактировать: Конечно, это просто полезно, когда вы создаете один из InputStream или OutputStream из файла. Используйте file.toPath(), чтобы получить путь из файла.

Чтобы записать в существующий файл (например, созданный с помощью File.createTempFile()), вам нужно передать опцию REPLACE_EXISTING copy (в противном случае FileAlreadyExistsException выдается):

Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING)
122 голосов
/ 12 сентября 2016

Java 9

Начиная с Java 9, InputStream предоставляет метод с именем transferTo со следующей подписью:

public long transferTo(OutputStream out) throws IOException

Как указано в документации , transferTo будет:

Считывает все байты из этого входного потока и записывает байты в заданный поток вывода в том порядке, в котором они читаются. По возвращении, это входной поток будет в конце потока. Этот метод не закрывается любой поток.

Этот метод может блокировать бесконечно чтение из входной поток или запись в выходной поток. Поведение для случай, когда входной и / или выходной поток асинхронно закрыт, или поток, прерванный во время передачи, является высоко входным и выходным специфичный для потока, и поэтому не указанный

Таким образом, чтобы записать содержимое Java InputStream в OutputStream, вы можете написать:

input.transferTo(output);
96 голосов
/ 04 сентября 2008

Я думаю, что это будет работать, но не забудьте проверить это ... незначительное "улучшение", но это может быть немного дороже при чтении.

byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
    out.write(buffer, 0, len);
}
47 голосов
/ 26 марта 2014

Использование гуавы ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);
25 голосов
/ 13 сентября 2013

Простая функция

Если вам нужно это только для записи InputStream в File, тогда вы можете использовать эту простую функцию:

private void copyInputStreamToFile( InputStream in, File file ) {
    try {
        OutputStream out = new FileOutputStream(file);
        byte[] buf = new byte[1024];
        int len;
        while((len=in.read(buf))>0){
            out.write(buf,0,len);
        }
        out.close();
        in.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
15 голосов
/ 12 февраля 2009

PipedInputStream и PipedOutputStream должны использоваться только при наличии нескольких потоков, как указано в Javadoc .

Также обратите внимание, что входные и выходные потоки не переносят прерывания потоков на IOException s ... Итак, вы должны рассмотреть возможность включения политики прерывания в свой код:

byte[] buffer = new byte[1024];
int len = in.read(buffer);
while (len != -1) {
    out.write(buffer, 0, len);
    len = in.read(buffer);
    if (Thread.interrupted()) {
        throw new InterruptedException();
    }
}

Это было бы полезным дополнением, если вы планируете использовать этот API для копирования больших объемов данных или данных из потоков, которые застревают на недопустимо долгое время.

14 голосов
/ 13 октября 2016

JDK использует тот же код, поэтому кажется, что нет "более простого" способа без неуклюжих сторонних библиотек (которые, вероятно, в любом случае не делают ничего другого). Следующее непосредственно скопировано из java.nio.file.Files.java:

// buffer size used for reading and writing
    private static final int BUFFER_SIZE = 8192;

/**
     * Reads all bytes from an input stream and writes them to an output stream.
     */
    private static long copy(InputStream source, OutputStream sink)
        throws IOException
    {
        long nread = 0L;
        byte[] buf = new byte[BUFFER_SIZE];
        int n;
        while ((n = source.read(buf)) > 0) {
            sink.write(buf, 0, n);
            nread += n;
        }
        return nread;
    }
11 голосов
/ 02 февраля 2017

Для тех, кто использует Spring Framework , есть полезный StreamUtils класс:

StreamUtils.copy(in, out);

Выше не закрывает потоки. Если вы хотите, чтобы потоки были закрыты после копирования, используйте FileCopyUtils class вместо:

FileCopyUtils.copy(in, out);
8 голосов
/ 04 сентября 2008

Нет способа сделать это намного проще с помощью методов JDK, но, как уже отмечал Apocalisp, вы не единственный, кто имеет такую ​​идею: вы можете использовать IOUtils из Jakarta Commons IO , у него также есть много других полезных вещей, которые IMO на самом деле должны быть частью JDK ...

...