Вопрос по c#, inputstream, bytearray – Как преобразовать поток в байт [] в C #? [Дубликат]

339

На этот вопрос уже есть ответ:

Создание байтового массива из потока 16 ответов

Есть ли простой способ или метод для преобразованияStream вbyte[] в C #?

Не совсем ответ на вопрос, но если ваш поток поступает из файла, вы можете использоватьFile.ReadAllBytes(path) чтобы получить массив байтов в одну строку. Jesus Jimenez

Ваш Ответ

12   ответов
6

public static Byte[] ToByteArray(this Stream stream) {
    Int32 length = stream.Length > Int32.MaxValue ? Int32.MaxValue : Convert.ToInt32(stream.Length);
    Byte[] buffer = new Byte[length];
    stream.Read(buffer, 0, length);
    return buffer;
}
Поток, возвращаемый, например, из Microsoft.SharePoint.Client.File.OpenBinaryDirect, очень часто возвращает только 500 или около того байтов за раз, независимо от размера вашего буфера. Вы никогда не должны игнорировать возвращаемое значение Stream.Read. Dylan Nicholson
етод @ Stream.Read может прочитать меньше байтов, чем вы запрашиваете. Вы должны проверить возвращаемое значение метода Read. arkhivania
Проблема в том, что Stream.Read не всегда читаетlength байтов - он может решить прочитать меньше (и возвращает фактическое количество прочитанных байтов). Выимею чтобы вызвать это в цикле, чтобы он работал универсально! JCH2k
Для этого метода и метода @ user734862 я получил следующую ошибку: «Этот поток не поддерживает операции поиска», исключение System.NotSupportedException. Я думаю, что это может быть связано с тем, что я читаю файл из http-местоположения и затем отправляю его обратно. Вполне может быть иначе, когда вы работаете с файлом в вашей системе. Daniel Hollinrake
Но посмотрите внимательно на код. Буфер создается на основе информации stream.Length. Это никогда не будет слишком большим для потока. Возможно, он слишком маленький (для него максимальный размер - Int32.MaxValue), но в большинстве случаев это маловероятно. Vinicius
27
    byte[] buf;  // byte array
    Stream stream=Page.Request.InputStream;  //initialise new stream
    buf = new byte[stream.Length];  //declare arraysize
    stream.Read(buf, 0, buf.Length); // read from stream to byte array
Если я правильно помню, «Чтение» не всегда считывает всю доступную сумму из потока - например, запрашивает N байтов, возвращает M байтов с M <N. Отсюда различные методы для создания буфера и чтения несколько раз. Msdn.microsoft.com / EN-US / библиотека / ... David Ford
16
Byte[] Content = new BinaryReader(file.InputStream).ReadBytes(file.ContentLength);
Я могу ошибаться, но это кажется более эффективным, чем способ MemoryStream, который создает две копии в памяти. Dan Randolph
Зависит от сценария, ваш пример очень специфичен для потока файлов, который вы можете определить длину контента. Как насчет того, если вход является потоком? поскольку Readbytes принимает только int32 Vincent
хорошо, ноBinaryReader одноразовый, поэтому для этого следует использоватьusing. Nyerguds
41

Stream класс имеет встроенныйCopyTo метод, который вы можете использовать.

Для более ранних версий фреймворка удобная вспомогательная функция:

public static void CopyStream(Stream input, Stream output)
{
    byte[] b = new byte[32768];
    int r;
    while ((r = input.Read(b, 0, b.Length)) > 0)
        output.Write(b, 0, r);
}

Затем используйте один из вышеперечисленных методов для копирования вMemoryStream и звониGetBuffer в теме

var file = new FileStream("c:\\foo.txt", FileMode.Open);

var mem = new MemoryStream();

// If using .NET 4 or later:
file.CopyTo(mem);

// Otherwise:
CopyStream(file, mem);

// getting the internal buffer (no additional copying)
byte[] buffer = mem.GetBuffer();
long length = mem.Length; // the actual length of the data 
                          // (the array may be longer)

// if you need the array to be exactly as long as the data
byte[] truncated = mem.ToArray(); // makes another copy

Редактировать Первоначально я предложил использовать ответ Джейсона дляStream, который поддерживаетLength свойство. Но у него был недостаток, потому что предполагалось, чStream вернул бы все его содержимое за одинRead, что не обязательно верно (не дляSocket, например.) Я не знаю, есть ли примерStream реализация в BCL, которая поддерживаетLength но может вернуть данные более короткими порциями, чем вы запрашиваете, но любой может унаследоватьStream это может быть легко.

В большинстве случаев, вероятно, проще использовать приведенное выше общее решение, но предположим, что вы действительно хотите читать непосредственно в массив, который являетсяbigEnough:

byte[] b = new byte[bigEnough];
int r, offset;
while ((r = input.Read(b, offset, b.Length - offset)) > 0)
    offset += r;

То есть, неоднократно звониRead и переместите позицию, в которой вы будете хранить данные.

Было бы очень интересно узнать причину отрицательных голосов! Daniel Earwicker
Зачем беспокоиться о потоке памяти, когда вы можете просто использовать List <byte> и AddRange ()? Насколько я знаю, под капотом все происходит точно так же. Tamas Czinege
@ DrJokepu - потому что потоковое копирование обычно полезно в других ситуациях. Вам нужно только написать этот один метод, и вы получите копирование потока в поток и копирование потока в массив. Daniel Earwicker
@ John Saunders - этот метод CopyStream определенно не должен содержать операторов, так что это был бы странный запрос. Пример использования может потребоваться для FileStream, но может и не понадобиться (зависит от того, хочет ли остальной код повторно использовать тот же FileStream). Daniel Earwicker
@ Earwicker: я не понизил голос, потому что вы любезно сделали изменение, которое я просил. OTOH, если бы я не спросил, я бы проголосовал за отсутствие «использования» отчетов. На самом деле я решил не упоминать об этом и в моей просьбе! John Saunders
722

using(var memoryStream = new MemoryStream())
{
  sourceStream.CopyTo(memoryStream);
  return memoryStream.ToArray();
}
Исправлено. В случае MemoryStream в этом нет особой необходимости (копаться в исходном коде, он ничего не делает), но это может измениться. James Dingle
Примечание: CopyTo доступен только в .NET Framework 4. Simon Mourier
MemoryStream являетсяIDisposable - не должно ли быть завернуто вusing? BlueRaja - Danny Pflughoeft
Да, это так. Вы можете использовать MemoryStream.GetBuffer (), чтобы избежать дополнительной копии, но помните, что размер возвращаемого массива не равен размеру данных. James Dingle
Если длина исходного потока известна заранее, лучше указать емкость MemoryStream с этой длиной; внутренний буфер будет иметь правильный размер. Если длина неизвестна, то запись в MemoryStream подразумевает наличие нескольких возможных копий внутреннего буфера по мере записи данных и расширения буфера, и в этом случае дополнительная копия ToArray не обязательно является основной проблемой. James Dingle
1
Stream s;
int len = (int)s.Length;
byte[] b = new byte[len];
int pos = 0;
while((r = s.Read(b, pos, len - pos)) > 0) {
    pos += r;
}

s.Length превышаетInt32.MaxValue. Но если вам нужно прочитать такой большой поток в память, вы можете подумать о другом подходе к вашей проблеме.

Редактировать: Если ваш поток не поддерживаетLength свойство, измените используя Earwicker's Обходной путь.

public static class StreamExtensions {
    // Credit to Earwicker
    public static void CopyStream(this Stream input, Stream output) {
        byte[] b = new byte[32768];
        int r;
        while ((r = input.Read(b, 0, b.Length)) > 0) {
            output.Write(b, 0, r);
        }
    }
}

[...]

Stream s;
MemoryStream ms = new MemoryStream();
s.CopyStream(ms);
byte[] b = ms.GetBuffer();
Он сказал, что прочитал. Он хотел преобразовать поток в byte [], который является Read, а не Write. John Saunders
Было бы здорово, если бы он сказал «Читай вместо записи Daniel Earwicker
Еще одна проблема с этим (я только что вспомнил) заключается в том, что метод Read может не вернуть все данные за один раз. Daniel Earwicker
2

    static byte[] StreamToByteArray(Stream inputStream)
    {
        if (!inputStream.CanRead)
        {
            throw new ArgumentException(); 
        }

        // This is optional
        if (inputStream.CanSeek)
        {
            inputStream.Seek(0, SeekOrigin.Begin);
        }

        byte[] output = new byte[inputStream.Length];
        int bytesRead = inputStream.Read(output, 0, output.Length);
        Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length");
        return output;
    }

Тест

    static void Main(string[] args)
    {
        byte[] data;
        string path = @"C:\Windows\System32\notepad.exe";
        using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
        {
            data = StreamToByteArray(fs);
        }

        Debug.Assert(data.Length > 0);
        Debug.Assert(new FileInfo(path).Length == data.Length); 
    }

Я бы спросил, почему вы хотите прочитать поток в байт [], если вы хотите скопировать содержимое потока, могу ли я предложить использовать MemoryStream и записать ваш входной поток в поток памяти.

Не все потоки поддерживают свойство Length John Saunders
Нет абсолютно никаких гарантий, что Read () вернет все байты для чтения. Stefan Schultze
Пост начинается быстро и грязно. В некоторых тривиальных случаях этого может быть достаточно. Oybek
1

массив байтов:

public byte[] StreamToByteArray(string fileName)
{
    byte[] total_stream = new byte[0];
    using (Stream input = File.Open(fileName, FileMode.Open, FileAccess.Read))
    {
        byte[] stream_array = new byte[0];
        // Setup whatever read size you want (small here for testing)
        byte[] buffer = new byte[32];// * 1024];
        int read = 0;

        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            stream_array = new byte[total_stream.Length + read];
            total_stream.CopyTo(stream_array, 0);
            Array.Copy(buffer, 0, stream_array, total_stream.Length, read);
            total_stream = stream_array;
        }
    }
    return total_stream;
}
146

Вызовите следующую функцию как

byte[] m_Bytes = StreamHelper.ReadToEnd (mystream);

Function:

public static byte[] ReadToEnd(System.IO.Stream stream)
    {
        long originalPosition = 0;

        if(stream.CanSeek)
        {
             originalPosition = stream.Position;
             stream.Position = 0;
        }

        try
        {
            byte[] readBuffer = new byte[4096];

            int totalBytesRead = 0;
            int bytesRead;

            while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
            {
                totalBytesRead += bytesRead;

                if (totalBytesRead == readBuffer.Length)
                {
                    int nextByte = stream.ReadByte();
                    if (nextByte != -1)
                    {
                        byte[] temp = new byte[readBuffer.Length * 2];
                        Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                        Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                        readBuffer = temp;
                        totalBytesRead++;
                    }
                }
            }

            byte[] buffer = readBuffer;
            if (readBuffer.Length != totalBytesRead)
            {
                buffer = new byte[totalBytesRead];
                Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
            }
            return buffer;
        }
        finally
        {
            if(stream.CanSeek)
            {
                 stream.Position = originalPosition; 
            }
        }
    }
Не уверен, что согласен с политикой расширения буфера Length * 2. Remus Rusanu
Да, это правда. Но я использую этот код в рамках 1.1 и 2.0. Потому что это так велико. Ваш ответ хорош для. pedrofernandes
Если вы хотите иметь возможность читать потоки произвольной длины, в основном все это необходимо. Вы можете использовать List <byte> и сохранить некоторый код .. Thorarin
Выглядит как модифицированная версия Yoda.arachsys.com / CSharp / readbinary.html SwDevMan81
Куча проблем смешана в одном большом методе. Да, все это должно быть сделано, но не все в одной функции. Есть растущий байтовый массив и поток чтения. Намного легче разобраться, если они разлучены. Daniel Earwicker
20

public static class StreamExtensions
{
    public static byte[] ReadAllBytes(this Stream instream)
    {
        if (instream is MemoryStream)
            return ((MemoryStream) instream).ToArray();

        using (var memoryStream = new MemoryStream())
        {
            instream.CopyTo(memoryStream);
            return memoryStream.ToArray();
        }
    }
}

Просто скопируйте класс в свое решение, и вы сможете использовать его в каждом потоке:

byte[] bytes = myStream.ReadAllBytes()

Works отлично подходит для всех моих потоков и сохраняет много кода! Конечно, вы можете изменить этот метод, чтобы использовать некоторые другие подходы для повышения производительности, если это необходимо, но мне нравится, чтобы он был простым.

3

если вы публикуете файл с мобильного устройства или другого

    byte[] fileData = null;
    using (var binaryReader = new BinaryReader(Request.Files[0].InputStream))
    {
        fileData = binaryReader.ReadBytes(Request.Files[0].ContentLength);
    }
Стоит упомянуть, что вы можете использовать это на любом FileStream. В WPF нельзя использоватьRequest.Files[0].InputStream, но ты можешь сделатьusing (FileStream fs = new File.OpenRead(fileName)) { var binaryReader = new BinaryReader(fs); fileData = binaryReader.ReadBytes((int)fs.Length); }. Спасибо за совет vapcguy
0

буфер должен быть «большим ebough», но правильный дизайн приложения должен включать транзакции и разделители. В этой конфигурации каждая транзакция будет иметь заданную длину, поэтому ваш массив будет ожидать определенное количество байтов и вставлять его в буфер правильного размера. Разделители будут обеспечивать целостность транзакции и будут предоставляться в каждой транзакции. Чтобы сделать ваше приложение еще лучше, вы можете использовать 2 канала (2 сокета). Можно было бы передавать транзакции сообщений управления фиксированной длиной, которые включали бы информацию о размере и порядковом номере транзакции данных, которая должна быть передана с использованием канала данных. Получатель подтвердит создание буфера, и только тогда данные будут отправлены. Если у вас нет контроля над отправителем потока, вам нужен многомерный массив в качестве буфера. Компонентные массивы будут достаточно маленькими, чтобы ими можно было управлять, и достаточно большими, чтобы быть практичными на основе вашей оценки ожидаемых данных. Логика процесса будет искать известные начальные и конечные разделители в последующих массивах элементов. Как только будет найден конечный разделитель, будет создан новый буфер для хранения релевантных данных между разделителями, и начальный буфер придется реструктурировать, чтобы разрешить удаление данных.

Насколько ниже код для преобразования потока в байтовый массив.

Stream s = yourStream;
int streamEnd = Convert.ToInt32(s.Length);
byte[] buffer = new byte[streamEnd];
s.Read(buffer, 0, streamEnd);

Похожие вопросы