[英]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.