今天我惊讶地发现我无法找到任何简单的方法来编写内容 InputStream 到一个 OutputStream 在爪哇。显然,字节缓冲区代码并不难编写,但我怀疑我只是错过了一些可以让我的生活更轻松(并且代码更清晰)的东西。

所以,给定一个 InputStream inOutputStream 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?

爪哇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 到一个 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();
    }
}

PipedInputStreamPipedOutputStream 仅当您有多个线程时才应使用,例如 Javadoc 指出.

另请注意,输入流和输出流不会将任何线程中断包装为 IOException是...因此,您应该考虑将中断策略合并到您的代码中:

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

对于那些使用 Spring框架 有一个有用的 流工具 班级:

StreamUtils.copy(in, out);

以上并没有关闭流。如果您希望复制后关闭流,请使用 文件复制工具 类代替:

FileCopyUtils.copy(in, out);

使用 JDK 方法无法更轻松地做到这一点,但正如 Apocalisp 已经指出的那样,您并不是唯一有这种想法的人:你可以使用 IOUtils雅加达公共 IO, ,它还有很多其他有用的东西,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();
    }

使用 Commons Net 的 Util 类:

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 I/O 实用程序:

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

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

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

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-orient-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