Convert StreamReader to byte[]
Asked Answered
C

7

59

I am getting the result StreamReader object.

I want to convert the result into byte[].

How can I convert StreamReaderto byte[]?

Thanks

Command answered 3/5, 2011 at 8:55 Comment(0)
C
47

A StreamReader is for text, not plain bytes. Don't use a StreamReader, and instead read directly from the underlying stream.

Calumny answered 3/5, 2011 at 8:59 Comment(1)
Uh, StreamReader reads a Stream. It doesn't matter if you are streaming text or bytes, it can still read it. If you use StreamReader and its methods (i.e. ReadLine() or ReadToEnd()) you ARE reading the underlying data of the stream.Stolon
I
75

Just throw everything you read into a MemoryStream and get the byte array in the end. As noted, you should be reading from the underlying stream to get the raw bytes.

var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
    var buffer = new byte[512];
    var bytesRead = default(int);
    while ((bytesRead = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
        memstream.Write(buffer, 0, bytesRead);
    bytes = memstream.ToArray();
}

Or if you don't want to manage the buffers:

var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
    reader.BaseStream.CopyTo(memstream);
    bytes = memstream.ToArray();
}
Incline answered 3/5, 2011 at 9:8 Comment(13)
Eh... Why the detour with the MemoryStream?Interlard
@Daniel: Well, unless you know the length of the stream beforehand you're going to have to read it into some kind of variable length thing. I guess MemoryStream is one choice. @Jeff: What's with the use of default()? I don't think it makes things any more readable.Calumny
@Daniel: I'm not sure what you mean by "detour," but as far as I can tell, the asker wants to create a byte array of the entire contents of the stream, not just part of it. Using a MemoryStream will manage the size for us as we read in the data.Incline
@Jeff: It just seems strange to use a MemoryStream for this when a simple List<byte> in conjunction with AddRange would suffice. It won't change the code much, but it just feels weird to me to use a memory stream here :-)Interlard
@Matti: It's just a coding style I picked up, I am embracing the var keyword whenever I can (for most local variables). I don't expect everyone to be comfortable with it but I'm ok with that.Incline
@Daniel: A MemoryStream is just a wrapper to a byte[]. A List<T> is just a wrapper to a T[]. It is much more appropriate to me, especially since we're already dealing with streams.Incline
@Daniel: A MemoryStream is likely to be slightly more efficient as it's designed to efficiently handle arrays of bytes. For instance, you can add only a part of an array with Write (unlike with AddRange on a List<T>), and it doesn't access byte arrays via an interface, which might be slightly less efficient. You can also get the underlying array directly with GetBuffer() without creating a copy of it. Of course, this is only important if you have a big amount of data.Calumny
@Jeff: I... really don't see the point of your weird var...default thing. It's making the code less readable and longer. Where exactly did you pick this up...?Calumny
@Matti: Just so you know, GetBuffer() will return the underlying byte array only if it was passed in to the constructor (the array the MemoryStream is wrapping). Otherwise, you'd have to use ToArray(). As for the var stuff, a lot of the code I deal with (particularly in other languages) use implicit typing and it's just a preference of mine to use it for local variables with exceptions for delegates/lambdas. It's kinda hard to explain so I'll just leave it at that. :)Incline
@Jeff: Actually, you can access the buffer even if you didn't pass in an array to the constructor. It will have extraneous data in it, though, so I guess it's only useful if you really really want to avoid allocations. But if you have that much data, you probably wouldn't want to read it all into memory at once anyways...Calumny
@Matti: Ah, I could have sworn that GetBuffer() would have thrown in this case. It appears it only does only in some select cases.Incline
@Jeff: The documentation is indeed a bit vague as to what a "a publicly visible buffer" is.Calumny
One might want to use await reader.BaseStream.CopyToAsync(…) instead of reader.BaseStream.CopyTo(…).Phonetics
C
47

A StreamReader is for text, not plain bytes. Don't use a StreamReader, and instead read directly from the underlying stream.

Calumny answered 3/5, 2011 at 8:59 Comment(1)
Uh, StreamReader reads a Stream. It doesn't matter if you are streaming text or bytes, it can still read it. If you use StreamReader and its methods (i.e. ReadLine() or ReadToEnd()) you ARE reading the underlying data of the stream.Stolon
C
3

You can also use CopyTo:

var ms = new MemoryStream();
yourStreamReader.BaseStream.CopyTo(ms); // blocking call till the end of the stream
ms.GetBuffer().CopyTo(yourArray, ms.Length);

or

var ms = new MemoryStream();
var ct = yourStreamReader.BaseStream.CopyToAsync(ms);
await ct;
ms.GetBuffer().CopyTo(yourArray, ms.Length);
Conciliatory answered 6/3, 2015 at 22:9 Comment(0)
C
2

As Matti Virkkunen pointed out, you don't neccessarily need the MemoryStream. When the stream is not too long you can write into an array directly. Less allocated memory is the advantage.

using (var stream = File.OpenRead("your_file.ext"))
{
    var length = stream.Length;
    if (length <= int.MaxValue)
    {
        var result = new byte[length];
        var bytesRead = stream.Read(result, 0, (int)length);
        if (bytesRead == length) return result;
    }
    //fallback
    using (var memoryStream = new MemoryStream())
    {
        stream.CopyTo(memoryStream);
        return memoryStream.ToArray();
    }
}
Cloistered answered 23/9, 2022 at 15:30 Comment(0)
U
1
        BlobClient blob = container.GetBlobClient(path);
        //blob.DownloadTo(@"temp\"+ file);
        var response = blob.DownloadContent();
        var stream = response.Value.Content.ToStream();
        using (var sr = new StreamReader(stream))
        {
            using (MemoryStream ms = new MemoryStream())
            {
                sr.BaseStream.CopyTo(ms);
                return Convert.ToBase64String(ms.ToArray()) ;
            }
        }
Underground answered 8/10, 2021 at 6:42 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Champignon
I
0

Since a few years have gone by, I think this implementation is better and simpler

using(var ms = new MemoryStream())
{
    await yourStreamReader.BaseStream.CopyToAsync(ms);
    //now you can do whatever you want with ms like returning it as a Base64 string
    return Convert.ToBase64String(ms.ToArray());
}
Indigene answered 11/12, 2023 at 19:21 Comment(0)
S
-6

For everyone saying to get the bytes, copy it to MemoryStream, etc. - if the content isn't expected to be larger than computer's memory should be reasonably be expected to allow, why not just use StreamReader's built in ReadLine() or ReadToEnd()? I saw these weren't even mentioned, and they do everything for you.

I had a use-case where I just wanted to store the path of a SQLite file from a FileDialogResult that the user picks during the synching/initialization process. My program then later needs to use this path when it is run for normal application processes. Maybe not the ideal way to capture/re-use the information, but it's not much different than writing to/reading from an .ini file - I just didn't want to set one up for one value. So I just read it from a flat, one-line text file. Here's what I did:

string filePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (!filePath.EndsWith(@"\")) temppath += @"\"; // ensures we have a slash on the end
filePath = filePath.Replace(@"\\", @"\"); // Visual Studio escapes slashes by putting double-slashes in their results - this ensures we don't have double-slashes
filePath += "SQLite.txt";

string path = String.Empty;
FileStream fs = new FileStream(filePath, FileMode.Open);
StreamReader sr = new StreamReader(fs);
path = sr.ReadLine();  // can also use sr.ReadToEnd();
sr.Close();
fs.Close();
fs.Flush();

return path;

If you REALLY need a byte[] instead of a string for some reason, using my example, you can always do:

byte[] toBytes;
FileStream fs = new FileStream(filePath, FileMode.Open);
StreamReader sr = new StreamReader(fs);
toBytes = Encoding.ASCII.GetBytes(path);
sr.Close();
fs.Close();
fs.Flush();

return toBytes;

(Returning toBytes instead of path.)

If you don't want ASCII you can easily replace that with UTF8, Unicode, etc.

Stolon answered 21/11, 2016 at 16:58 Comment(11)
answer unrelated to the questionUniversally
@MikeKeskinov Really? Did you even read my answer fully? Do you know how to read code? The OP wanted to convert a Stream to bytes. This answer shows how to take a file, read it into a Stream, then read the text into path, a string variable (because why would you ever need bytes when you want to read text from a file?!). But I also included how to convert that string to bytes, if bytes were truly needed.Stolon
The original question didn't mention reading text. It could be a binary file that is being read. You mention using Encoding.ASCII.GetBytes(...), but a quick look at the documentation for that shows it mentioning 7-bit, so the input may well get mangled. Similarly, UTF-8 encoding makes assumptions about the input that also don't hold when fed with arbitrary binary files.Reyreyes
@Reyreyes You're correct, but that's up to the developer to know what their input is and adjust the Encoding for it, in the first place-kinda a given. The OP on this question didn't give a lot of information on where the data was being read from or its format, so to build an answer, I had to make an assumption it was text in a text file, and even (as a bonus!) included that bit about getting bytes. If it's bytes, there's of course more than just Encoding.ASCII.GetBytes(), but Encoding.UTF8.GetBytes(), Encoding.Unicode.GetBytes(), etc. Use the intellisense before just ragging on an answer.Stolon
@Stolon This code can work depending on context. Other solutions will always work regardless of context. I didn't downvote you, but I can understand why people would prefer a solution that always works vs a solution that sometimes works.Ansilma
@Ansilma The only time it won't is if you have a file that exceeds the memory (RAM) of your system, so it will work in 99.9% of the cases. I find it hard to believe a programmer will have a file like that on the daily, but if they do, they can create a buffer system, instead, like in the accepted answer. I didn't want to repeat what was already offered.Stolon
You can't treat binary files as text files and say it will work 99.9% of the time. It will work 99.9% of the time for specific file types, yes. Which, by definition, is less appropriate than using a technique which will always work. As per my previous comment.Ansilma
@Ansilma When can't you treat a binary file as text? Isn't it just 0s and 1s?Stolon
haacked.com/archive/2012/01/30/… is one example of why treating binary files as string does not always work as you might hope @StolonAnsilma
@Ansilma Not a bad point, but the link you cited uses UTF-8, whereas I have it using ASCII & that was only if they REALLY found my 2nd method necessary, because the main idea in my post was to avoid the encodings altogether by doing StreamReader and a sr.ReadLine to string. Plus, your link only talks about it losing bytes after doing a round-trip if you do a GetString then a GetBytes. I'm not doing that at all - it does the GetBytes from the StreamReader, which is EXACTLY HOW you are SUPPOSED to do it & lets it be stored as bytes, afterwards. You can stream bytes all day.Stolon
@Stolon Fair enough. Have a great day.Ansilma

© 2022 - 2024 — McMap. All rights reserved.