簡體   English   中英

從 stream 創建字節數組

[英]Creating a byte array from a stream

從輸入 stream 創建字節數組的首選方法是什么?

這是我當前使用 .NET 3.5 的解決方案。

Stream s;
byte[] b;

using (BinaryReader br = new BinaryReader(s))
{
    b = br.ReadBytes((int)s.Length);
}

讀取和寫入 stream 的塊仍然是更好的主意嗎?

這實際上取決於您是否可以信任s.Length 對於許多流,您只是不知道會有多少數據。 在這種情況下 - 在 .NET 4 之前 - 我會使用這樣的代碼:

public static byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

對於 .NET 4 及更高版本,我將使用Stream.CopyTo ,它基本上等同於我的代碼中的循環 - 創建MemoryStream ,調用stream.CopyTo(ms)然后返回ms.ToArray() 任務完成。

我或許應該解釋一下為什么我的答案比其他答案長。 Stream.Read不保證它會讀取它要求的所有內容。 例如,如果您正在從網絡流中讀取數據,它可能會讀取一個數據包的價值然后返回,即使很快就會有更多數據。 BinaryReader.Read將一直持續到流結束或您指定的大小,但您仍然必須知道開始時的大小。

上述方法將繼續讀取(並復制到MemoryStream ),直到數據用完為止。 然后它要求MemoryStream返回一個數組中的數據副本。 如果您知道開始的大小 - 或者認為您知道大小,但不確定 - 您可以將MemoryStream構造為開始時的大小。 同樣,您可以在末尾進行檢查,如果流的長度與緩沖區的大小相同(由MemoryStream.GetBuffer返回),則您可以返回緩沖區。 所以上面的代碼沒有完全優化,但至少是正確的。 它不承擔關閉流的任何責任 - 調用者應該這樣做。

有關更多信息(以及替代實現),請參閱本文

雖然 Jon 的回答是正確的,但他正在重寫CopyTo中已經存在的代碼。 因此,對於 .Net 4,請使用 Sandip 的解決方案,但對於 .Net 的先前版本,請使用 Jon 的答案。 Sandip 的代碼將通過使用“使用”來改進,因為CopyTo中的異常在許多情況下很可能並且不會處理MemoryStream

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

只是想指出,如果你有一個 MemoryStream 你已經有了memorystream.ToArray()

此外,如果您正在處理未知或不同子類型的流,並且您可以收到MemoryStream ,您可以在這些情況下中繼所述方法,並仍然使用其他人接受的答案,如下所示:

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();

只是我的幾分錢......我經常使用的做法是將這樣的方法組織為自定義助手

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

將命名空間添加到配置文件並在您希望的任何地方使用它

您可以簡單地使用 MemoryStream 類的 ToArray() 方法,例如

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();

你甚至可以通過擴展讓它更漂亮:

namespace Foo
{
    public static class Extensions
    {
        public static byte[] ToByteArray(this Stream stream)
        {
            using (stream)
            {
                using (MemoryStream memStream = new MemoryStream())
                {
                     stream.CopyTo(memStream);
                     return memStream.ToArray();
                }
            }
        }
    }
}

然后將其作為常規方法調用:

byte[] arr = someStream.ToByteArray()

如果有人喜歡它,這里有一個僅 .NET 4+ 的解決方案,作為擴展方法形成,無需對 MemoryStream 進行不必要的 Dispose 調用。 這是一個無可救葯的微不足道的優化,但值得注意的是,未能處理 MemoryStream 並不是真正的失敗。

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        var ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

我收到 Bob 的(即提問者的)代碼編譯時錯誤。 Stream.Length 是一個 long 而 BinaryReader.ReadBytes 接受一個整數參數。 就我而言,我不希望處理大到需要長精度的流,因此我使用以下內容:

Stream s;
byte[] b;

if (s.Length > int.MaxValue) {
  throw new Exception("This stream is larger than the conversion algorithm can currently handle.");
}

using (var br = new BinaryReader(s)) {
  b = br.ReadBytes((int)s.Length);
}

上面的那個沒問題……但是當你通過 SMTP 發送東西時你會遇到數據損壞(如果你需要的話)。 我已更改為有助於正確發送字節的其他內容:'

using System;
using System.IO;

        private static byte[] ReadFully(string input)
        {
            FileStream sourceFile = new FileStream(input, FileMode.Open); //Open streamer
            BinaryReader binReader = new BinaryReader(sourceFile);
            byte[] output = new byte[sourceFile.Length]; //create byte array of size file
            for (long i = 0; i < sourceFile.Length; i++)
                output[i] = binReader.ReadByte(); //read until done
            sourceFile.Close(); //dispose streamer
            binReader.Close(); //dispose reader
            return output;
        }'

創建一個輔助類並在您希望使用它的任何地方引用它。

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

在命名空間 RestSharp.Extensions 中有 ReadAsBytes 方法。 在此方法中使用了 MemoryStream,並且與此頁面上的某些示例中的代碼相同,但是當您使用 RestSharp 時,這是最簡單的方法。

using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();

這是我正在使用、測試和運行良好的功能。 請記住,'input' 不應為空,並且 'input.position' 應在讀取之前重置為 '0',否則它將中斷讀取循環,並且不會讀取任何內容以轉換為數組。

    public static byte[] StreamToByteArray(Stream input)
    {
        if (input == null)
            return null;
        byte[] buffer = new byte[16 * 1024];
        input.Position = 0;
        using (MemoryStream ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
            byte[] temp = ms.ToArray();

            return temp;
        }
    }

您可以使用此擴展方法。

public static class StreamExtensions
{
    public static byte[] ToByteArray(this Stream stream)
    {
        var bytes = new List<byte>();

        int b;
        while ((b = stream.ReadByte()) != -1)
            bytes.Add((byte)b);

        return bytes.ToArray();
    }
}

由於此答案沒有現代(即異步)版本,因此這是我用於此目的的擴展方法:

public static async Task<byte[]> ReadAsByteArrayAsync(this Stream source)
{
    // Optimization
    if (source is MemoryStream memorySource)
        return memorySource.ToArray();

    using var memoryStream = new MemoryStream();
    await source.CopyToAsync(memoryStream);
    return memoryStream.ToArray();
}

優化基於ToArray的源代碼調用一些內部方法這一事實。

如果流支持 Length 屬性,則可以直接從中創建字節數組。 優點是MemoryStream.ToArray創建數組兩次,加上緩沖區中可能有一些未使用的額外字節。 此解決方案分配所需的確切數組。 如果流不支持 Length 屬性,它將拋出NotSupportedException異常。

還值得注意的是,數組不能大於 int.MaxValue。

public static async Task<byte[]> ToArrayAsync(this Stream stream)
    {
        if (stream.Length > int.MaxValue)
        {
            throw new ArgumentOutOfRangeException("Cannot convert stream larger than max value of signed integer (2 147 483 647) to array");
        }

        var array = new byte[stream.Length];
        await stream.ReadAsync(array, 0, (int)stream.Length);
        return array;
    }
public static byte[] ToByteArray(Stream stream)
    {
        if (stream is MemoryStream)
        {
            return ((MemoryStream)stream).ToArray();
        }
        else
        {
            byte[] buffer = new byte[16 * 1024];
            using (MemoryStream ms = new MemoryStream())
            {
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                return ms.ToArray();
            }
        }            
    }

我能夠讓它在一行上工作:

byte [] byteArr= ((MemoryStream)localStream).ToArray();

正如johnnyRose所澄清的,上面的代碼只適用於 MemoryStream

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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