How to get a stream of an in memory string? - c#

I have a string of data, I want to have a stream of it without saving it to storage as a file and then getting an stream of it. then I will send this stream to SkyDrive. anyway.
await client.UploadAsync(folderId, filename, stream, OverwriteOption.Overwrite);
Is there anyway to get a stream of an in-memory string value, without saving it to storage first?

You can write it into a MemoryStream:
using(var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(yourString);
writer.Flush();
stream.Position = 0;
await client.UploadAsync(folderId, filename, stream, OverwriteOption.Overwrite);
}

Sure, use the MemoryStream class. You could load it like this for example:
var stream = new MemoryStream(Encoding.ASCII.GetBytes("Here is my string."));
or, you can just Write new data to it like this:
var myString = "Hello, World!";
stream.Write(Encoding.ASCII.GetBytes(myString), 0, myString.Length);
You could of course use a different encoding if you needed to, say UTF8 for example.

Related

Form Recognizer SDK, analyze with custom model, file stream issue

When I'm trying to analyze a pdf document using FileStream from a local file, everything works fine.
But when I use a IFormFile and use method OpenReadStream() and pass that stream to the Analyze method for form analyzer, i get an exception. I also tried creating a new stream out of the IFromFile stream and that did not work either.
Any help will be much appreciated. Thank you
Working code:
using var stream = new FileStream("D:\\somefile.pdf", FileMode.Open);
var result = await _formRecognizerClient.AnalyzeWithCustomModelAsync(modelId, fileStream, "application/pdf");
Code I am trying to make work:
using var stream = file.OpenReadStream(); // file is an IFormFile
var result = await _formRecognizerClient.AnalyzeWithCustomModelAsync(modelId, stream , file.ContentType);
I have a solution for now, its not elegant but it works. I am of course very much looking for something better if anyone can help.
For now, I am creating a file, saving it and creating a FileStream out of it. Also works in docker as I'm testing using docker-compose
var iFormFileStream = file.OpenReadStream();
var stream = File.Create(string.Format("tempfilename.pdf", File.));
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(stream);
stream.Close();
using var fileStream = new FileStream("tempfilename.pdf", FileMode.Open);
var result = await _formRecognizerClient.AnalyzeWithCustomModelAsync(modelId, fileStream, "application/pdf");

How to create a stream wrapping stream that can transform a stream

var incomingStream = ...
var outgoingStream = ...
await incomingStream.CopyToAsync(outgoingStream);
The above code is simple enough, and copies a incoming stream to the outgoign stream. Both streams being chunked transfers coming/going over the interet.
Now, lets say i wanted to Transform the stream with something like Func<Stream,Stream,Task> how would I do that without reading all data in.
Ofcause I could just do
var ms = new MemoryStream();
incomingStream.CopyTo(ms);
--- do transform of streams and seek
ms.CopyTo(outgoingStream)
but that would read the hole thing into the ms, is there any build in stuff that allows me to read from incoming stream and write to a new stream that dont buffer everything up but instead just keep a small internal stream for buffered data and it wont read from incoming stream before data is pulled off it again.
What I am trying to do is:
protected async Task XmlToJsonStream(Stream instream, Stream outStream)
{
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = false;
var reader = XmlReader.Create(instream, readerSettings);
var jsonWriter = new JsonTextWriter(new StreamWriter(outStream));
jsonWriter.WriteStartObject();
while (await reader.ReadAsync())
{
jsonWriter.writeReader(reader);
}
jsonWriter.WriteEndObject();
jsonWriter.Flush();
}
protected async Task XmlFilterStream(Stream instream, Stream outStream)
{
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = false;
var reader = XmlReader.Create(instream, readerSettings);
var writer = XmlWriter.Create(outStream, new XmlWriterSettings { Async = true, CloseOutput = false })
while (reader.Read())
{
writer.writeReader(reader);
}
}
but i dont know how to hook it up.
var incomingStream = ...
var outgoingStream = ...
var temp=...
XmlFilterStream(incomingStream,temp);
XmlToJsonStream(temp,outgoingstream);
because if I use a MemoryStream as temp, would it not just at the end have it all stored in the stream. Looking for at stream that throws away the data again when it has been read.
All of the above is just example code, missing some disposes and seeks ofcause, but I hope I managed to illustrate what i am going for. To be able to based on settings to plug and play between just copying stream, doing xml filtering and optional transform it to json.
Streams are sequences of bytes, so a stream transformation would be something like Func<ArraySegment<byte>, ArraySegment<byte>>. You can then apply it in a streaming way:
async Task TransformAsync(this Stream source, Func<ArraySegment<byte>, ArraySegment<byte>> transform, Stream destination, int bufferSize = 1024)
{
var buffer = new byte[bufferSize];
while (true)
{
var bytesRead = await source.ReadAsync(buffer, 0, bufferSize);
if (bytesRead == 0)
return;
var bytesToWrite = transform(new ArraySegment(buffer, 0, bytesRead));
if (bytesToWrite.Count != 0)
await destination.WriteAsync(bytesToWrite.Buffer, bytesToWrite.Offset, bytesToWrite.Count);
}
}
It's a bit more complicated than that, but that's the general idea. It needs some logic to ensure WriteAsync writes all the bytes; and there's also usually a "flush" method that is required in addition to the transform method, which is called when the source stream finishes, so the transform algorithm has a last chance to return its final data to write to the output stream.
If you want streams of other things, like XML or JSON types, then you're probably better off going with Reactive Extensions.
I'm not sure I understand your question fully, but I think you're asking how you would operate on an input stream without loading it entirely into memory first.
In this case, you wouldn't want do do something like this:
var ms = new MemoryStream();
incomingStream.CopyTo(ms);
This does load the entire input stream incomingStream into memory -- into ms.
From what I can see, your XmlFilterStream method seems to be redundant, i.e. XmlToJsonStream does everything that XmlFilterStream does anyway.
Why not just have:
protected async Task XmlToJsonStream(Stream instream, Stream outStream)
{
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = false;
var reader = XmlReader.Create(instream, readerSettings);
var jsonWriter = new JsonTextWriter(new StreamWriter(outStream));
jsonWriter.WriteStartObject();
while (await reader.ReadAsync())
{
jsonWriter.writeReader(reader);
}
jsonWriter.WriteEndObject();
jsonWriter.Flush();
}
And call it like this:
var incomingStream = ...
var outgoingStream = ...
XmlToJsonStream(incomingStream ,outgoingstream);
If the answer is that you have omitted some important details in XmlFilterStream then, without seeing those details, I would recommend that you just integrate those into the one XmlToJsonStream function.

Capture Stream Output to String

I'm calling a library method that writes to a stream. But I want to write to a string. Is this possible? (I do not control the source code of the method I'm calling and so changing that is not an option.)
Experimenting, I tried something like this:
iCalendarSerializer serializer = new iCalendarSerializer();
MemoryStream stream = new MemoryStream();
serializer.Serialize(new iCalendar(), stream, System.Text.Encoding.UTF8);
byte[] buff = new byte[stream.Length];
stream.Read(buff, 0, (int)stream.Length);
But I get an error on the last line that's something about not being able to access a closed stream. Apparently, the Serialize() method closes the stream when it's done.
Are there other options?
How about byte[] buff = stream.ToArray()?
ToArray is one of 2 correct way of getting the data out of memory stream (the other one is GetBuffer and Length). It looks like you just want byte array sized to data of the stream and ToArray does exactly that.
Note that it is by design safe to call these 3 methods on disposed stream, so you can safely wrap using(stream) around the code that write some data to the stream.
In you case stream look to be disposed by serialization code (.Serialize).
iCalendarSerializer serializer = new iCalendarSerializer();
MemoryStream stream = new MemoryStream();
using(stream)
{
serializer.Serialize(new iCalendar(), stream, System.Text.Encoding.UTF8);
}
byte[] buff = stream.ToArray();
In your example you need to change the position of the stream before read takes place:
stream.Position = 0;
stream.Read(buff, 0, (int)stream.Length);
In order to write stream to string you can use StreamReader.ReadToEnd() method:
var reader = new StreamReader(stream);
var text = reader.ReadToEnd();

Cannot access a closed Stream while creating a downloadable text file in ASP MVC 3

Im trying to prompt a downloadable text file (.txt), but I get this error:
Cannot access a closed Stream.
I have looked at simular questions in here:
Cannot Access Closed Stream
But it was not very useful.
Here is my code:
private FileStreamResult Export()
{
string name = "filename.txt";
MemoryStream stream = new MemoryStream();
using (StreamWriter writer = new StreamWriter(stream))
{
StringBuilder sb = new StringBuilder();
sb.Append("A text...");
writer.WriteLine(sb.ToString());
}
return File(stream, "text/plain", name);
}
UPDATE (working copy):
This gives me an blank text file.
private FileResult Export()
{
string name = "filename.txt";
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
StringBuilder sb = new StringBuilder();
sb.Append("A text...");
writer.WriteLine(sb.ToString());
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
return File(stream, "text/plain", name);
}
That is correct, when you wrap a stream in another stream, calling .Close() or .Dispose() on any of them will dispose the whole stream. In this case, wrapping the MemoryStream in a StreamWriter means that when the using statement completes the StreamWriter and MemoryStream are both disposed.
My guess is since you are returning a FileStreamResult the encapsulating File will close the stream for you after the stream is no longer used. In this case, you do not want to use the using statement and will want to leave the stream open when returning it.
UPDATE
Since a stream is forward access you'll need to see the stream back to the beginning to allow the data to be read back out.
stream.Seek(0, SeekOrigin.Begin);
Just remove that using statement - you are passing disposed object reference to File method and you that's the reason why you get exception. From MSDN,
The StreamWriter object calls Dispose on the provided Stream object
when StreamWriter.Dispose is called.
I believe File will dispose stream by itself after usage (not verified by looking at source code).
UPDATE:
writer.Flush(); before return statement should help you
You have to set the position of the memorystream to 0 before using it in your FileStreamResult, otherwise it will be read from current position (IE the end of the stream).
stream.Position = 0;
return File(stream, "text/plain", name);
Just had the same thing.
I know this thread is ancient, just hoping to aid others having the same issue.
Replace the FileStreamResult type on your action with FileResult.

unable to save dynamically created MemoryStream with rebex sftp

I'm using StreamWriter to generate a dynamic file and holding it in a MemoryStream. Everything appears to be alright until I go to save the file using rebex sftp.
The example they give on their site works fine:
// upload a text using a MemoryStream
string message = "Hello from Rebex FTP for .NET!";
byte[] data = System.Text.Encoding.Default.GetBytes(message);
System.IO.MemoryStream ms = new System.IO.MemoryStream(data);
client.PutFile(ms, "message.txt");
However the code below does not:
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
writer.Write("test");
}
client.PutFile(stream, "test.txt");
}
The file "test.txt" is saved, however it is empty. Do I need to do more than just enable AutoFlush for this to work?
After writing to the MemoryStream, the stream is positioned at the end. The PutFile method reads from the current position to the end. That's exactly 0 bytes.
You need to position the stream at the beginning before passing it to PutFile:
...
}
stream.Seek(0, SeekOrigin.Begin);
client.PutFile(stream, "test.txt");
You may also need to prevent the StreamWriter from disposing the MemoryStream:
var writer = new StreamWriter(stream);
writer.Write("test");
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
client.PutFile(stream, "test.txt");

Categories