簡體   English   中英

將 Java InputStream 的內容寫入 OutputStream 的簡單方法

[英]Easy way to write contents of a Java InputStream to an OutputStream

今天我驚訝地發現,在 Java 中,我無法找到將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);
}

正如 WMR 所提到的,來自 Apache 的org.apache.commons.io.IOUtils有一個名為copy(InputStream,OutputStream) ,它完全符合您的要求。

所以你有了:

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

...在你的代碼中。

您是否有理由避免使用IOUtils

如果您使用的是 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)

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

JDK使用相同的代碼,所以如果沒有笨重的第三方庫(它們可能不會做任何不同的事情),似乎沒有“更簡單”的方法。 以下是直接從java.nio.file.Files.java復制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類:

StreamUtils.copy(in, out);

以上不會關閉流。 如果您希望在復制后關閉流,請改用FileCopyUtils類:

FileCopyUtils.copy(in, out);

Javadoc 所述PipedInputStreamPipedOutputStream僅應在您有多個線程時使用。

另外,請注意,輸入流和輸出流不會用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 方法沒有辦法更輕松地做到這一點,但正如 Apocalisp 已經指出的那樣,您並不是唯一一個有這個想法的人:您可以使用Jakarta Commons IO 的IOUtils ,它還有很多其他有用的東西, IMO實際上應該是JDK的一部分......

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

這是我如何使用最簡單的 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);
    }
}

使用 Commons Net 的 Util 類:

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

恕我直言,更小的片段(也更窄地限定了長度變量):

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從代碼中刪除緩沖語義

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

這是我最好的鏡頭!!

並且不要使用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
    );
}

我認為最好使用大緩沖區,因為大多數文件都大於 1024 字節。 此外,檢查讀取字節數是否為正數也是一個好習慣。

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

可讀性不強,但有效,沒有依賴關系,可以在任何 Java 版本上運行

byte[] buffer = new byte[1024];
for (int n; (n = inputStream.read(buffer)) != -1; outputStream.write(buffer, 0, n));

另一個可能的候選者是 Guava I/O 實用程序:

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

我想我會使用這些,因為 Guava 在我的項目中已經非常有用,而不是為一個函數添加另一個庫。

PipedInputStream 和 PipedOutputStream 可能會有用,因為您可以將一個連接到另一個。

我使用了ByteStreamKt.copyTo(src, dst, buffer.length)方法

這是我的代碼

public static void replaceCurrentDb(Context context, Uri newDbUri) {
    try {
        File currentDb = context.getDatabasePath(DATABASE_NAME);
        if (currentDb.exists()) {
            InputStream src = context.getContentResolver().openInputStream(newDbUri);
            FileOutputStream dst = new FileOutputStream(currentDb);
            final byte[] buffer = new byte[8 * 1024];
            ByteStreamsKt.copyTo(src, dst, buffer.length);
            src.close();
            dst.close();
            Toast.makeText(context, "SUCCESS! Your selected file is set as current menu.", Toast.LENGTH_LONG).show();
        }
        else
            Log.e("DOWNLOAD:::: Database", " fail, database not found");
    }
    catch (IOException e) {
        Toast.makeText(context, "Data Download FAIL.", Toast.LENGTH_LONG).show();
        Log.e("DOWNLOAD FAIL!!!", "fail, reason:", e);
    }
}
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 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM