Простой способ записать содержимое 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);
}
Решение
Если вы используете 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.
Также обратите внимание, что входные и выходные потоки не переносят никаких прерываний потока с 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 для копирования больших объемов данных или данных из потоков, которые застревают на невыносимо долгое время.
Тот Самый 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){}
}