关于IO:将Java输入流的内容写入输出流的简单方法

Easy way to write contents of a Java InputStream to an OutputStream

今天,我很惊讶地发现,在Java中,EDCOX1(0)的内容不能用任何简单的方式写入EDCOX1(1)。显然,字节缓冲区代码并不难写,但我怀疑我只是缺少了一些可以让我的生活更轻松(代码也更清晰)的东西。

那么,考虑到一个InputStreamin和一个OutputStreamout的情况,有没有更简单的方法来写下以下内容?

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


正如WMR所提到的,Apache中的org.apache.commons.io.IOUtils有一个名为copy(InputStream,OutputStream)的方法,它可以完全满足您的需求。

所以,你有:

1
2
3
4
5
InputStream in;
OutputStream out;
IOUtils.copy(in,out);
in.close();
out.close();

…在你的代码中。

你为什么要避开IOUtils


如果使用Java 7,文件(在标准库中)是最好的方法:

1
2
3
/* 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):

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


爪哇9

自Java 9以来,EDCOX1(6)提供了一种名为EDCOX1×16的方法,具有以下签名:

1
public long transferTo(OutputStream out) throws IOException

如文件所述,transferTo将:

Reads all bytes from this input stream and writes the bytes to the
given output stream in the order that they are read. On return, this
input stream will be at end of stream. This method does not close
either stream.

This method may block indefinitely reading from the
input stream, or writing to the output stream. The behavior for the
case where the input and/or output stream is asynchronously closed, or
the thread interrupted during the transfer, is highly input and output
stream specific, and therefore not specified

因此,为了将一个Java EDCOX1的6个内容写入EDCOX1的19个方面,您可以编写:

1
input.transferTo(output);


我认为这是可行的,但一定要测试它…小的"改进",但在可读性方面可能会有一些代价。

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


使用Guava的ByteStreams.copy()

1
ByteStreams.copy(inputStream, outputStream);


简单函数

如果只需要将InputStream写入File中,那么可以使用此简单函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
    }
}


正如javadoc所指出的,只有在有多个线程时,才应该使用PipedInputStreamPipedOutputStream

另外,请注意,输入流和输出流不会用IOExceptions包装任何线程中断。因此,您应该考虑在代码中加入中断策略:

1
2
3
4
5
6
7
8
9
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复制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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;
    }


对于使用Spring框架的用户,有一个有用的streamutils类:

1
StreamUtils.copy(in, out);

上述情况并不能关闭河流。如果希望在复制后关闭流,请改用FileCopyUtils类:

1
FileCopyUtils.copy(in, out);


没有办法用JDK方法更容易做到这一点,但是正如apocalisp已经指出的,你不是唯一一个有这个想法的人:你可以使用雅加达公共IO的ioutils,它也有很多其他有用的东西,即imo实际上应该是JDK的一部分……


使用Java7并尝试使用资源,附带了一个简单易读的版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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();
    }


使用commons net的util类:

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

下面是我使用最简单的for循环的方式。

1
2
3
4
5
6
7
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字节。另外,检查读取字节数是否为正数也是一个好的实践。

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


一个imho最小片段(也更窄地限定长度变量):

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

作为旁注,我不明白为什么更多的人不使用for循环,而是选择带有赋值和测试表达式的while,有些人认为这是"糟糕"的风格。


我使用BufferedInputStreamBufferedOutputStream从代码中删除缓冲语义。

1
2
3
4
5
6
7
try (OutputStream out = new BufferedOutputStream(...);
     InputStream in   = new BufferedInputStream(...))) {
  int ch;
  while ((ch = in.read()) != -1) {
    out.write(ch);
  }
}

另一个可能的备选方案是Guava I/O实用程序:

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

我想我会使用这些,因为在我的项目中,guava已经非常有用了,而不是为一个函数添加另一个库。


pipedinputstream和pipedoutputstream可能有一些用途,因为您可以将两者连接起来。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}


试试Cactoos:

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

更多详情请访问:http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html


你可以用这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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){}
 }