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

StackOverflow https://stackoverflow.com/questions/43157

  •  09-06-2019
  •  | 
  •  

Вопрос

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

Решение

Если вы используете 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 опция копирования (в противном случае 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?

Java 9

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

public long transferTo(OutputStream out) throws IOException

В качестве Документация состояния, transferTo будет:

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

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

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

input.transferTo(output);

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

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

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

ByteStreams.copy(inputStream, outputStream);

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

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

Для тех, кто использует Пружинный каркас есть полезный StreamUtils ( Потоковые утилиты ) класс:

StreamUtils.copy(in, out);

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

FileCopyUtils.copy(in, out);

Нет способа сделать это намного проще с помощью методов JDK, но, как уже отметил Apocalisp, вы не единственный, у кого есть эта идея:Вы могли бы использовать Учетные записи От Джакартский Коммонс ИО, в нем также есть много других полезных вещей, которые IMO на самом деле должны быть частью JDK...

Используя Java7 и попробуйте-с-ресурсами, поставляется с упрощенной и читаемой версией.

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

Используйте Util-класс Commons Net от 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);
    }
}

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

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

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

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

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

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

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

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

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

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

try (OutputStream out = new BufferedOutputStream(...);
     InputStream in   = new BufferedInputStream(...))) {
  int ch;
  while ((ch = in.read()) != -1) {
    out.write(ch);
  }
}
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;
}

Попробуй Какту:

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

Более подробная информация здесь: http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html

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

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){}
 }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top