Легкий способ записи содержимого 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);
}

Ответ 1

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

/* 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 copy (иначе FileAlreadyExistsException):

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

Ответ 2

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

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

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

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

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

Ответ 3

Java 9

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

public long transferTo(OutputStream out) throws IOException

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

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

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

Поэтому, чтобы написать содержимое Java InputStream для OutputStream, вы можете написать:

input.transferTo(output);

Ответ 4

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

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

Ответ 5

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

ByteStreams.copy(inputStream, outputStream);

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

StreamUtils.copy(in, out);

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

FileCopyUtils.copy(in, out);

Ответ 10

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

Ответ 11

Используя 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();
    }

Ответ 12

Использовать класс сообщества Net Util:

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

Ответ 13

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

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);
    }
}

Ответ 14

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

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

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

Ответ 15

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

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

Ответ 16

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

Ответ 17

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

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

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

Ответ 18

Я использую BufferedInputStream и BufferedOutputStream чтобы удалить семантику буферизации из кода

try (OutputStream out = new BufferedOutputStream(...);
     InputStream in   = new BufferedInputStream(...))) {
  int ch;
  while ((ch = in.read()) != -1) {
    out.write(ch);
  }
}

Ответ 19

Это мой лучший снимок !!

И не используйте inputStream.transferTo(...) потому что он слишком общий. Ваша производительность кода будет лучше, если вы будете контролировать свою буферную память.

public static void transfer(InputStream in, OutputStream out, int buffer) throws IOException {
    byte[] read = new byte[buffer]; // Your buffer size.
    while (0 < (buffer = in.read(read)))
        out.write(read, 0, buffer);
}

Я использую его с этим (улучшаемым) методом, когда заранее знаю размер потока.

public static void transfer(int size, InputStream in, OutputStream out) throws IOException {
    transfer(in, out,
            size > 0xFFFF ? 0xFFFF // 16bits 65,536
                    : size > 0xFFF ? 0xFFF// 12bits 4096
                            : size < 0xFF ? 0xFF // 8bits 256
                                    : size
    );
}

Ответ 20

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

Ответ 22

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

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){}
 }