How to merge two memory streams? - c#

I have two MemoryStream instances.
How to merge them into one instance?
Well, now I can't copy from one MemoryStream to another.
Here is a method:
public static Stream ZipFiles(IEnumerable<FileToZip> filesToZip) {
ZipStorer storer = null;
MemoryStream result = null;
try {
MemoryStream memory = new MemoryStream(1024);
storer = ZipStorer.Create(memory, GetDateTimeInRuFormat());
foreach (var currentFilePath in filesToZip) {
string fileName = Path.GetFileName(currentFilePath.FullPath);
storer.AddFile(ZipStorer.Compression.Deflate, currentFilePath.FullPath, fileName,
GetDateTimeInRuFormat());
}
result = new MemoryStream((int) storer.ZipFileStream.Length);
storer.ZipFileStream.CopyTo(result); //Does not work!
//result's length will be zero
}
catch (Exception) {
}
finally {
if (storer != null)
storer.Close();
}
return result;
}

Spectacularly easy with CopyTo or CopyToAsync:
var streamOne = new MemoryStream();
FillThisStreamUp(streamOne);
var streamTwo = new MemoryStream();
DoSomethingToThisStreamLol(streamTwo);
streamTwo.CopyTo(streamOne); // streamOne holds the contents of both
The framework, people. The framework.

Based on the answer shared by #Will above, here is complete code:
void Main()
{
var s1 = GetStreamFromString("Hello");
var s2 = GetStreamFromString(" World");
var s3 = s1.Append(s2);
Console.WriteLine(Encoding.UTF8.GetString((s3 as MemoryStream).ToArray()));
}
public static Stream GetStreamFromString(string text)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(text);
writer.Flush();
stream.Position = 0;
return stream;
}
public static class Extensions
{
public static Stream Append(this Stream destination, Stream source)
{
destination.Position = destination.Length;
source.CopyTo(destination);
return destination;
}
}
And merging stream collection with async:
async Task Main()
{
var list = new List<Task<Stream>> { GetStreamFromStringAsync("Hello"), GetStreamFromStringAsync(" World") };
Stream stream = await list
.Select(async item => await item)
.Aggregate((current, next) => Task.FromResult(current.Result.Append(next.Result)));
Console.WriteLine(Encoding.UTF8.GetString((stream as MemoryStream).ToArray()));
}
public static Task<Stream> GetStreamFromStringAsync(string text)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(text);
writer.Flush();
stream.Position = 0;
return Task.FromResult(stream as Stream);
}
public static class Extensions
{
public static Stream Append(this Stream destination, Stream source)
{
destination.Position = destination.Length;
source.CopyTo(destination);
return destination;
}
}

Create third(let it be mergedStream) MemoryStream with length
equal to sum of first and second lengths
Write first MemoryStream to mergedStream (use GetBuffer() to
get byte[] from MemoryStream)
Write second MemoryStream to mergedStream(use GetBuffer())
Remember about offset while writing.
It's rather append, but it's totally unclear what is merge operation on MemoryStreams

Related

MemoryStream.ToArray() return empty array if StreamWriter is not disposed

I'm trying to get array of bytes from my model to put it in the file. I have a method as such:
public static byte[] GetByteArray(List<MyModel> models)
{
using var ms = new MemoryStream();
using var sw = new StreamWriter(ms);
foreach (var model in models)
{
sw.Write(model.Id + "," + model.Name);
sw.WriteLine();
}
sw.Dispose();
return ms.ToArray();
}
This method works fine, but as may think I don't need to dispose StreamWriter manually, cause I have a using statement. I thought as well, but when I remove sw.Dispose(); the ms.ToArray(); returns an empty array. Can someone explain this behavior to me?
You have the line:
using var sw = new StreamWriter(ms);
This only disposes the StreamWriter at the end of the method. However you're calling ms.ToArray() before the end of the method. This means that you're calling ms.ToArray() before the StreamWriter is disposed.
However, the StreamWriter is buffering some data internally, and only flushes this out to the MemoryStream when it is disposed. You therefore need to make sure you dispose the StreamWriter before calling ms.ToArray().
It's probably clearer to use the older using syntax, which is explicit about when the disposal happens:
public static byte[] GetByteArray(List<MyModel> models)
{
using var ms = new MemoryStream();
using (var sw = new StreamWriter(ms))
{
foreach (var model in models)
{
sw.Write(model.Id + "," + model.Name);
sw.WriteLine();
}
}
return ms.ToArray();
}
The dispose does part of the job. It flushes the writer. Use Flush() to flush it manually.
public static byte[] GetByteArray(List<MyModel> models)
{
var ms = new MemoryStream();
using var sw = new StreamWriter(ms);
foreach (var model in models)
{
sw.Write(model.Id + "," + model.Name);
sw.WriteLine();
}
// flush the writer, to make sure it is written to the stream.
sw.Flush();
return ms.ToArray();
}
You don't need to dispose the memory stream, because the StreamWriter takes ownership.
I don't like the construct that the streamwriter takes ownage of the memory stream. This is probably because there the streamwriter can also be used directly on a file. A constructor which has a file path as parameter. (so no stream parameter is needed)
StreamWriter leaveOpen constructor
If you writing List<MyModel> items as strings, you can simplify conversion by:
public static byte[] GetByteArray(List<MyModel> models) =>
Encoding.UTF8.GetBytes(string.Join(Environment.NewLine,
models.Select(model => $"{model.Id},{model.Name}")));
Or use third-party serializers, such from Newtonsoft.Json (example from here):
public static byte[] Serialize<T>(this T source)
{
var asString = JsonConvert.SerializeObject(source, SerializerSettings);
return Encoding.Unicode.GetBytes(asString);
}
public static T Deserialize<T>(this byte[] source)
{
var asString = Encoding.Unicode.GetString(source);
return JsonConvert.DeserializeObject<T>(asString);
}
As the others have mentioned you have to Flush the StreamWriter
This is what your function looks like:
public static byte[] GetByteArray(List<MyModel> models)
{
MemoryStream memoryStream = new MemoryStream();
try
{
StreamWriter streamWriter = new StreamWriter(memoryStream);
try
{
List<MyModel>.Enumerator enumerator = models.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
MyModel current = enumerator.Current;
streamWriter.Write(string.Concat(current.Id, ",", current.Name));
streamWriter.WriteLine();
}
}
finally
{
((IDisposable)enumerator).Dispose();
}
streamWriter.Dispose();
return memoryStream.ToArray();
}
finally
{
if (streamWriter != null)
{
((IDisposable)streamWriter).Dispose();
}
}
}
finally
{
if (memoryStream != null)
{
((IDisposable)memoryStream).Dispose();
}
}
}

Value already read, or no value when trying to read from a Stream

I've been trying this for a long time but it keeps giving me an error. I have an array of bytes that should represent a nbt document. I would like to convert this into a c# object with a library: fNbt.
Here is my code:
byte[] buffer = Convert.FromBase64String(value);
byte[] decompressed;
using (var inputStream = new MemoryStream(buffer))
{
using var outputStream = new MemoryStream();
using (var gzip = new GZipStream(inputStream, CompressionMode.Decompress, leaveOpen: true))
{
gzip.CopyTo(outputStream);
}
fNbt.NbtReader reader = new fNbt.NbtReader(outputStream, true);
var output = reader.ReadValueAs<AuctionItem>(); //Error: Value already read, or no value to read.
return output;
}
When I try this, it works:
decompressed = outputStream.ToArray();
outputStream.Seek(0, SeekOrigin.Begin);
outputStream.Read(new byte[1000], 0, decompressed.Count() - 1);
But when I try this, it doesn't:
outputStream.Seek(0, SeekOrigin.Begin);
fNbt.NbtReader reader = new fNbt.NbtReader(outputStream, true);
reader.ReadValueAs<AuctionItem>();
NbtReader, like most stream readers, begins reading from the current position of whatever stream you give it. Since you're just done writing to outputStream, then that position is the stream's end. Which means at that point there's nothing to be read.
The solution is to seek the outputStream back to the beginning before reading from it:
outputStream.Seek(0, SeekOrigin.Begin); // <-- seek to the beginning
// Do the read
fNbt.NbtReader reader = new fNbt.NbtReader(outputStream, true);
var output = reader.ReadValueAs<AuctionItem>(); // No error anymore
return output;
The solution is as follows. NbtReader.ReadValueAs does not consider a nbtCompound or nbtList as value. I made this little reader but it is not done yet (I will update the code once it is done).
public static T ReadValueAs<T>(string value) where T: new()
{
byte[] buffer = Convert.FromBase64String(value);
using (var inputStream = new MemoryStream(buffer))
{
using var outputStream = new MemoryStream();
using (var gzip = new GZipStream(inputStream, CompressionMode.Decompress, leaveOpen: true))
{
gzip.CopyTo(outputStream);
}
outputStream.Seek(0, SeekOrigin.Begin);
return new EasyNbt.NbtReader(outputStream).ReadValueAs<T>();
}
}
This is the NbtReader:
private MemoryStream MemStream { get; set; }
public NbtReader(MemoryStream memStream)
{
MemStream = memStream;
}
public T ReadValueAs<T>() where T: new()
{
return ReadTagAs<T>(new fNbt.NbtReader(MemStream, true).ReadAsTag());
}
private T ReadTagAs<T>(fNbt.NbtTag nbtTag)
{
//Reads to the root and adds to T...
}

convert C++ istringstream usage to C#

I am rewriting some code and wondering how I can convert this from C++ to C#:
istringstream vertexString = new istringstream(s);
//number of vertices
int numVertices;
vertexString>>numVertices;
Add this to a static string utility class:
public static Stream ToStream(this string str)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(str);
writer.Flush();
stream.Position = 0;
return stream;
}
Use like this. s must be string.
int numVertices;
using (var stringStream = s.ToStream())
{
stringStream>>numVertices;
}

Why StringWriter.ToString return `System.Byte[]` and not the data?

UnZipFile method writes the data from inputStream to outputWriter.
Why sr.ToString() returns System.Byte[] and not the data?
using (var sr = new StringWriter())
{
UnZipFile(response.GetResponseStream(), sr);
var content = sr.ToString();
}
public static void UnZipFile(Stream inputStream, TextWriter outputWriter)
{
using (var zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if ((currentEntry = zipStream.GetNextEntry()) != null)
{
var size = 2048;
var data = new byte[size];
while (true)
{
size = zipStream.Read(data, 0, size);
if (size > 0)
{
outputWriter.Write(data);
}
else
{
break;
}
}
}
}
}
The problem is on the line:
outputWriter.Write(data);
StringWriter.Write has no overload expecting a byte[]. Therefore, Write(Object) is called instead. And according to MSDN:
Writes the text representation of an object to the text string or stream by calling the ToString method on that object.
Calling ToString on a byte array returns System.byte[], explaining how you get that string in your StringWriter.
The reason is simple:
data is of type byte[]. There is no overload for byte[] on StringWriter so it uses the overload for object. And then calls ToString() on the boxed byte array which simply prints the type.
Your code is equivalent to this:
outputWriter.Write(data.ToString());
theateist,
Looking at the other answers here, I am going to have to agree that the reason for the "ToString()" returning System.Byte[] is because that is what you are putting into it, and everything put into the StringWriter calls it's own "ToString" method when doing so. (i.e. byte[].toString() = "System.byte[]"). In fact the whole idea is that the StringWriter is only ment for writing into a string "buffer" (StringBuilder), so in theory if your file was large enough(bigger than 2048), your output would be "System.Byte[]System.Byte[]" (etc.). Try this to deflate into a memory stream and then read from that stream, may be a better understanding of what you are looking at. (Code not tested, just example).
using (Stream ms = new MemoryStream())
{
UnZipFile(response.GetResponseStream(), ms);
string content;
ms.Position = 0;
using(StreamReader s = new StreamReader(ms))
{
content = s.ReadToEnd();
}
}
public static void UnZipFile(Stream inputStream, Stream outputWriter)
{
using (var zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if ((currentEntry = zipStream.GetNextEntry()) != null)
{
int size = 2048;
byte[] data = new byte[size];
while (true)
{
size = zipStream.Read(data, 0, size);
if (size > 0)
{
outputWriter.Write(data);
}
else
{
break;
}
}
}
}
}
Another idea would actually be to using the endcoding to get the string
public string UnZipFile(Stream inputStream)
{
string tmp;
using(Stream zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if(currentEntry = zipStream.GetNextEntry()) != null)
{
using(Stream ms = new MemoryStream())
{
int size = 2048;
byte[] data = new byte[size];
while(true)
{
if((size = zipStream.Read(data,0,size)) > 0)
ms.Write(data);
else
break;
}
tmp = Encoding.Default.GetString(ms.ToByteArray());
}
}
}
}
return tmp;
}
Or as one last idea, you could actually change your original code to have
outputWriter.Write(Encoding.Default.GetString(data));
Instead of
outputWriter.Write(data);
By the way, please avoid the var keyword in posts, maybe just my pet peev, but code is less readable when utilizing weak types.
StringWriter.Write:MSDN
StringWriter.ToString:MSDN

object to byte not working

I am trying to convert class to byte but not worked.
I receive a byte[] (b) with zero length . why ?
void start()
{
Item item = new Item();
item.files.Add(#"test");
byte[] b = ObjectToByteArray(item); // b will have zero length
}
[Serializable]
public class Item : ISerializable
{
public Item()
{
files = new List<string>();
Exclude = false;
CleanEmptyFolder = false;
}
public List<string> files;
public string MusicProfileName;
public bool Exclude;
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("files", files);
info.AddValue("MusicProfileName", MusicProfileName);
info.AddValue("Exclude", Exclude);
}
#endregion
}
public byte[] ObjectToByteArray(object _Object)
{
using (var stream = new MemoryStream())
{
// serialize object
var formatter = new BinaryFormatter();
formatter.Serialize(stream, _Object);
// get a byte array
var bytes = new byte[stream.Length];
using (BinaryReader br = new BinaryReader(stream))
{
bytes = br.ReadBytes(Convert.ToInt32(stream.Length));
}
return bytes;
}
}
You didn't reset the MemoryStream before you started to read from it. The position is at the end of the stream, so you get an empty array.
Use the ToArray method instead, it gets the entire content regardless of the current position:
public byte[] ObjectToByteArray(object _Object) {
using (var stream = new MemoryStream()) {
// serialize object
var formatter = new BinaryFormatter();
formatter.Serialize(stream, _Object);
// get a byte array
return stream.ToArray();
}
}
Try using MemoryStream.ToArray instead of trying to read from the MemoryStream with a BinaryReader:
return stream.ToArray();
The problem is that you aren't resetting the stream, so it's trying to read from the end instead of the beginning. You could also seek back to the beginning of the stream:
stream.Seek(0, SeekOrigin.Begin);

Categories