스트림에서 바이트 배열 만들기


913

입력 스트림에서 바이트 배열을 만드는 데 선호되는 방법은 무엇입니까?

다음은 .NET 3.5의 현재 솔루션입니다.

Stream s;
byte[] b;

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

스트림 덩어리를 읽고 쓰는 것이 여전히 더 좋은 아이디어입니까?


60
물론, 또 다른 질문은 해야 당신이 스트림에서 바이트 [] 만들 ... 큰 데이터, 음, 스트림으로 스트림을 처리하는 것이 바람직하다!
Marc Gravell

2
실제로 바이트 [] 대신 스트림을 사용해야합니다. 그러나 스트림을 지원하지 않는 일부 시스템 API가 있습니다. 예를 들어 스트림에서 X509Certificate2 를 만들 수 없으면 byte [] (또는 문자열)를 지정해야합니다. 이 경우 x509 인증서가 큰 데이터 가 아니기 때문에 괜찮습니다 .
0xced

답변:


1294

그것은 당신이 믿을 수 있는지 아닌지에 달려 있습니다 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) 버퍼를 반환 할 수 있습니다. 따라서 위의 코드는 최적화되지 않았지만 적어도 정확합니다. 스트림을 닫을 책임은 없습니다. 호출자가 그렇게해야합니다.

자세한 내용 및 대체 구현에 대해서는 이 기사 를 참조하십시오 .


9
@ 존, 그것은 가치가 언급 될 수있다 yoda.arachsys.com/csharp/readbinary.html
샘 사프란

6
@Jeff : 여기에 컨텍스트가 없지만 스트림에 쓰고 있다면 읽기 전에 "되감기"해야합니다. 스트림 내 어디에 있는지 말하는 단 하나의 "커서"가 있습니다. 읽기 용이 아니라 쓰기 용이 아닙니다.
Jon Skeet

5
@Jeff : 발신자의 책임입니다. 결국, 스트림을 검색 할 수 없거나 (예 : 네트워크 스트림) 단순히 되감기 할 필요가 없습니다.
Jon Skeet

18
16*1024구체적으로 물어봐도 될까요?
Anyname Donotcare

5
@ just_name : 이것이 의미가 있는지 모르겠지만 (16 * 1024)는 Int16.MaxValue의 절반입니다 :)
caesay

734

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

6
당신의 대답과 Jon의 차이점은 무엇입니까? 또한 CopyTo가 작동하려면이 입력을 수행해야합니다 .Position = 0.
Jeff

1
@nathan, 웹 클라이언트에서 파일을 읽습니다 (filizesize = 1mb)-iis는 전체 1MB를 메모리에 올바르게로드해야합니까?
Royi Namir

5
@ Jeff, 내 대답은 .Net 4 이상에서만 작동하며 Jons는 이후 버전에서 제공되는 기능을 다시 작성하여 하위 버전에서 작동합니다. Seekable 스트림이 있고 처음부터 복사하려는 경우 코드 나 입력을 사용하여 처음으로 이동할 수있는 경우 CopyTo는 현재 위치에서만 복사하는 것이 정확합니다 .Seek (0, SeekOrigin.Begin), 많은 경우 스트림이 검색되지 않을 수 있습니다.
Nathan Phillips

5
input이미 MemorySteam단락되어 있는지 확인하는 것이 좋습니다. 나는 발신자가 어리석은 짓을한다는 것을 알고 MemoryStream있지만 ...
Jodrell

3
@Jodrell, 그렇습니다. 당신이 메모리에 작은 스트림의 수백만을 복사하고 그 중 하나가있는 경우 MemoryStream다음 최적화가 당신의 맥락에서 의미가 있는지 여부의 하나 복사하는 데 걸리는 시간에 대해 형식 변환의 수백만을 수행하는 데 걸리는 시간을 비교 한 MemoryStream로를 다른 MemoryStream.
Nathan Phillips

114

이미 가지고있는 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);
    }
}

1
허, 모든 공감대는 무엇입니까? 가장 관대 한 가정에도 불구하고 이것은 이미 존재하는 스트림에서만 작동합니다 MemoryStream. 물론 예제는 초기화되지 않은 변수를 사용하는 방법에 있어서도 불완전합니다.
로마 스타 코프

3
그렇습니다. 지적 해 주셔서 감사합니다. 포인트는 여전히 MemoryStream을 의미하므로이를 반영하도록 수정했습니다.
Fernando Neira

MemoryStream의 또 다른 가능성은 MemoryStream.GetBuffer ()이지만, 몇 가지 문제가 있습니다. 참조 stackoverflow.com/questions/1646193/...krishnabhargav.blogspot.dk/2009/06/...
RenniePet

4
이것은 실제로 Skeet의 코드에 버그를 도입합니다. 당신이 호출 할 경우 stream.Seek(1L, SeekOrigin.Begin)당신은 내의 readFully 호출하기 전에 스트림은 메모리 스트림 인 경우, 당신은 다른 스트림의 경우 1보다 더 많은 바이트를 얻을 것이다. 호출자가 현재 위치가 스트림의 끝까지 인 곳에서 읽을 것으로 예상하는 경우 CopyTo또는 ToArray(); 대부분의 경우 이것은 문제가되지 않지만 호출자가이 기발한 동작에 대해 알지 못하면 혼동 될 것입니다.
회복과

67
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();

9
메모리 조각화를 피하려면 "new MemoryStream (file.PostedFile.ContentLength)"을 사용하여 MemoryStream을 만들어야합니다.
Dan Randolph 2016 년

52

내 커플 센트 ... 내가 자주 사용하는 연습은 이와 같은 방법을 사용자 정의 도우미로 구성하는 것입니다.

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

구성 파일에 네임 스페이스를 추가하고 원하는 곳에서 사용하십시오.


5
4.0까지는 CopyTo사용할 수 없었으므로 .NET 3.5 이하에서는 작동 하지 않습니다 Stream.
Tim

16

예를 들어 MemoryStream 클래스의 ToArray () 메서드를 사용할 수 있습니다.

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

10

확장 기능으로 더 멋지게 만들 수도 있습니다.

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()

67
입력 스트림을 using 블록에 넣는 것은 나쁜 생각이라고 생각합니다. 그 책임은 전화 절차에 달려 있습니다.
Jeff

7

Bob 's (즉, 질문자) 코드에서 컴파일 시간 오류가 발생합니다. Stream.Length는 길지만 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);
}

5

누구나 좋아하는 경우 MemoryStream에서 불필요한 Dispose 호출없이 확장 방법으로 형성된 .NET 4+ 전용 솔루션이 있습니다. 이것은 절망적으로 사소한 최적화이지만 MemoryStream을 처리하지 못하는 것이 실제로 실패하지는 않습니다.

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

3

위의 내용은 괜찮습니다 ...하지만 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;
        }'

이 코드가 데이터 손상을 피하는 위치를 알 수 없습니다. 설명 할 수 있습니까?
Nippey

사진이 있고 SMTP를 통해 사진을 보내려고한다고 가정 해 봅시다. 아마도 base64 인코딩을 사용하게 될 것입니다. 어떤 이유로 든 파일을 바이트로 나누면 파일이 손상됩니다. 그러나 이진 판독기를 사용하면 파일을 성공적으로 보낼 수 있습니다.
NothinRandom

3
다소 오래되었지만 나는 이것이 언급 할 것이라고 느꼈습니다. @ NothinRandom 구현은 스트림이 아닌 문자열로 작동합니다. 이 경우 File.ReadAllBytes를 사용하는 것이 가장 간단합니다.
XwipeoutX

1
위험한 코드 스타일로 인해 다운 보트가 발생합니다 (자동 폐기 / 사용 없음).
arni

슬프게도 -1 만 허용, 질문, input이라는 파일 이름 매개 변수, 폐기하지 않음, 읽기 버퍼 없음, 파일 모드 없음 및 바이트 단위로 바이트를 읽는 이진 판독기와 관련이없는 이유는 무엇입니까?
Aridane Álamo

2

도우미 클래스를 만들고 사용하려는 곳이면 어디에서나 참조하십시오.

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

2

네임 스페이스 RestSharp.Extensions에는 ReadAsBytes 메소드가 있습니다. 이 메서드 안에는 MemoryStream이 사용되고이 페이지의 일부 예제와 같은 코드가 있지만 RestSharp를 사용하는 경우 가장 쉬운 방법입니다.

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

1

이 확장 방법을 사용할 수 있습니다.

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

1

이것은 내가 사용하고 테스트하고 잘 작동 한 기능입니다. 'input'은 null이 아니어야하며 '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;
        }
    }

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

귀중한 것을 추가하지 않고 # 1 및 # 3 답변의 코드를 복사했습니다. 그렇게하지 마십시오. :)
CodeCaster

코드를 추가 할 때 제안 된 솔루션도 곧 설명하십시오.
yakobom

-5

한 줄로 작동시킬 수있었습니다.

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

johnnyRose 에 의해 명확하게 , 위의 코드는 MemoryStream에서만 작동합니다


2
그렇지 localStream않으면 MemoryStream어떻게됩니까? 이 코드는 실패합니다.
johnnyRose

localStream은 스트림 기반 객체 여야합니다. 스트림 기반 객체에 대한 자세한 내용은 stackoverflow.com/questions/8156896/…
Abba

1
내가 제안려고하면 캐스트하려고하면, 인 localStreamA와 MemoryStream있지만 localStream입니다 하지MemoryStream , 그것은 것입니다 실패합니다. 이 코드는 잘 컴파일되지만 실제 유형에 따라 런타임에 실패 할 수 localStream있습니다. 항상 기본 유형을 하위 유형으로 캐스트 할 수는 없습니다. 자세한 내용은 여기를 참조하십시오 . 이것은 항상 이것을 할 수 없는지 를 설명하는 또 다른 좋은 예 입니다 .
johnnyRose

위의 의견을 자세히 설명하면 모든 MemoryStream은 Stream이지만 모든 Stream이 MemoryStream은 아닙니다.
johnnyRose

모든 스트림 기반 객체에는 기본 유형으로 스트림이 있습니다. 스트림 자체는 항상 메모리 스트림으로 변환 할 수 있습니다. 어떤 스트림 기반 객체를 Meomry Stream으로 캐스팅하려고해도 항상 작동해야합니다. 우리의 목표는 스트림 객체를 바이트 배열로 변환하는 것입니다. 실패 할 수있는 중요한 사건을 알려 주실 수 있습니까?
Abba
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.