Простой способ записи содержимого Java InputStream в OutputStream

Я был удивлен, обнаружив сегодня, что я не мог отследить простой способ написать содержимое 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);
}

20 ответов


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

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

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

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

Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING)

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

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

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

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

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


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

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

Java 9

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

public long transferTo(OutputStream out) throws IOException

как документация государства transferTo будет:

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

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

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

input.transferTo(output);

используя гуава-х ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);

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

Если вам нужно только это написать 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();
    }
}

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

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

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 для копирования больших объемов данных или данных из потоков, которые застревают для невыносимо долго.


на 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;
    }

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


использование Java7 и try-with-resources, поставляется с упрощенной и читаемый вариант.

    try(InputStream inputStream     =   new FileInputStream("C:\mov.mp4");
        OutputStream outputStream   =   new FileOutputStream("D:\mov.mp4")){

        byte[] buffer    =   new byte[10*1024];

        for (int length; (length = inputStream.read(buffer)) != -1; ){
            outputStream.write(buffer, 0, length);
        }

    }catch (FileNotFoundException exception){
        exception.printStackTrace();
    }catch (IOException ioException){
        ioException.printStackTrace();
    }

для тех, кто использует весенние рамки есть полезное StreamUtils класс:

StreamUtils.copy(in, out);

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

FileCopyUtils.copy(in, out);

используйте класс Util Commons Net:

import org.apache.commons.net.io.Util;
...
Util.copyStream(in, out);

вот как я делаю с простейшим циклом for.

private void copy(final InputStream in, final OutputStream out)
    throws IOException {
    final byte[] b = new byte[8192];
    for (int r; (r = in.read(b)) != -1;) {
        out.write(b, 0, r);
    }
}

Я думаю, что лучше использовать большой буфер, потому что большинство файлов больше 1024 байт. Также хорошей практикой является проверка количества прочитанных байтов на положительный результат.

byte[] buffer = new byte[4096];
int n;
while ((n = in.read(buffer)) > 0) {
    out.write(buffer, 0, n);
}
out.close();

IMHO более минимальный фрагмент (который также более узко охватывает переменную длины):

byte[] buffer = new byte[2048];
for (int n = in.read(buffer); n >= 0; n = in.read(buffer))
    out.write(buffer, 0, n);

в качестве примечания, я не понимаю, почему больше людей не используют for петли, предпочитая ему while с выражением assign-and-test, которое некоторые считают "плохим" стилем.


PipedInputStream и PipedOutputStream могут быть полезны, так как вы можете подключить один к другому.


другим возможным кандидатом являются утилиты ввода-вывода Guava:

http://code.google.com/p/guava-libraries/wiki/IOExplained

Я думал, что буду использовать их, так как Guava уже очень полезен в моем проекте, а не добавлять еще одну библиотеку для одной функции.


попробовать Cactoos:

new LengthOf(new TeeInput(input, output)).value();

подробнее здесь: http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html


public static boolean copyFile(InputStream inputStream, OutputStream out) {
    byte buf[] = new byte[1024];
    int len;
    long startTime=System.currentTimeMillis();

    try {
        while ((len = inputStream.read(buf)) != -1) {
            out.write(buf, 0, len);
        }

        long endTime=System.currentTimeMillis()-startTime;
        Log.v("","Time taken to transfer all bytes is : "+endTime);
        out.close();
        inputStream.close();

    } catch (IOException e) {

        return false;
    }
    return true;
}

вы можете использовать этот метод

public static void copyStream(InputStream is, OutputStream os)
 {
     final int buffer_size=1024;
     try
     {
         byte[] bytes=new byte[buffer_size];
         for(;;)
         {
           int count=is.read(bytes, 0, buffer_size);
           if(count==-1)
               break;
           os.write(bytes, 0, count);
         }
     }
     catch(Exception ex){}
 }