I have this scenario:
DownloadLibrary.GetData(Stream targetStream);
SaveLibrary.WriteData(Stream sourceStream);
I want to send the data that targetStream collects to the sourceStream. I've come up with some solutions but I cannot find a way to connect those streams directly.
What I'm trying to achieve is send the data from targetStream to sourceStream without buffer the targetStream first.
How can it be done?
Thanks in advance.
There is built in support (from .Net 4.0) in Stream for copying one stream to another via CopyTo, e.g.:
stream1.CopyTo(stream2)
Example:
[Test]
public void test()
{
string inString = "bling";
byte[] inBuffer = Encoding.ASCII.GetBytes(inString);
Stream stream1 = new MemoryStream(inBuffer);
Stream stream2 = new MemoryStream();
//Copy stream 1 to stream 2
stream1.CopyTo(stream2);
byte[] outBuffer = new byte[inBuffer.Length];
stream2.Position = 0;
stream2.Read(outBuffer, 0, outBuffer.Length);
string outString = Encoding.ASCII.GetString(outBuffer);
Assert.AreEqual(inString, outString, "inString equals outString.");
}
The built-in CopyTo method referred to in chibacity's answer is available from .NET 4.0.
For earlier versions look at this question.
Related
I was wondering if someone can help me solve a issue I have run into while playing with FileStreams. I have been trying to send an integer, 50, to a FileStream and write its value onto a File. However, it writes 2 to the file instead of 50. I know the ASCII representation of 50 is 2, so am not sure if this is part of the issue. If anyone has any pointers, I'd really appreciate it!
Here is my relevant code:
From the main function:
string testMessage = "Testing writing some arbitrary string to a streama";
int tmL = testMessage.Length;
byte bb = Convert.ToByte(tmL);
SendByteStrem(bb);
And here is my streaming function:
public static void SendByteStrem(byte c){
using (Stream ioStream = new FileStream(#"C:\Users\db0201\Desktop\stream.txt", FileMode.OpenOrCreate)){
ioStream.WriteByte(c);
}
}
As you haven't explicitly stated your goal, i will answer the question for what it is.
The easiest way to write to a file would be to use File.WriteAllText which essentially opens a StreamWriter (which in-turn is open a FileStream) and calls Write
Creates a new file, write the contents to the file, and then closes
the file. If the target file already exists, it is overwritten.
File.WriteAllText(fileName, "50")
or
var myInt = 50;
File.WriteAllText(fileName, myInt.ToString())
If you wanted to use the StreaWriter exclusively
using (varwriter = new StreamWriter(fileName))
writer.Write(myInt.ToString());
If you wanted more configuration over the underlying FileStream
using (var writer = new StreamWriter(new FileStream(fileName, FileMode.CreateNew)))
writer.Write(myInt.ToString());
if you just want to use a FileStream then things get a bit more manual as you will need to convert things to bytes
using (var stream = new FileStream(fileName, FileMode.CreateNew))
{
var bytes = Encoding.UTF8.GetBytes(myInt.ToString());
stream.Write(bytes, 0, bytes.Length);
}
I am completely new to working with audio. I eventually want to stream an MP3 to a web page and allow user to alter the tempo. I got a HTML5 audio element set up and it can stream a MP3 fine. I can import the MP3 into NAudio.AudioFileReader and stream that to the page and that also works fine using the following code:
string fn = Server.MapPath("~/Uploads/Music/" + filename);
AudioFileReader reader = new AudioFileReader(fn);
MemoryStream outputStream = new MemoryStream();
using (NAudio.Wave.WaveFileWriter waveFileWriter = new WaveFileWriter(outputStream, reader.WaveFormat))
{
byte[] bytes = new byte[reader.Length];
reader.Position = 0;
reader.Read(bytes, 0, (int)reader.Length);
waveFileWriter.Write(bytes, 0, bytes.Length);
waveFileWriter.Flush();
}
return File(outputStream.ToArray(), "audio/mp3");
I'm not even sure if this is the proper way to do this, but I modified some code I found online and this does work. However, when looking at the NAudio Varispeed demo which integrates the SoundTouch library and trying to incorporate it, it no longer works.
I modified my code like this:
string fn = Server.MapPath("~/Uploads/Music/" + filename);
AudioFileReader reader = new AudioFileReader(fn);
bool useTempo = true;
VarispeedSampleProvider speedControl = new VarispeedSampleProvider(reader, 100, new SoundTouchProfile(useTempo, false));
MemoryStream outputStream = new MemoryStream();
using (NAudio.Wave.WaveFileWriter waveFileWriter = new WaveFileWriter(outputStream, reader.WaveFormat))
{
byte[] bytes = new byte[reader.Length];
speedControl.Read(bytes.Select(b => (float)Convert.ToDouble(b)).ToArray(), 0, (int)reader.Length);
waveFileWriter.Write(bytes, 0, bytes.Length);
waveFileWriter.Flush();
}
return File(outputStream.ToArray(), "audio/mp3");
It builds and appears like it's working but when I hit play, I get no audio.
What am I doing wrong here? Is this not even a good way to accomplish what I want?
You are reading into a temporary array (created by ToArray), so the audio you read is lost.
Instead, declare a float[], read into that, and then write the contents of that into the waveFileWriter.
Also, it is very important to use the return value from Read which will indicate the number of samples actually written into the array.
I'm using PUT method to update some data. But my below code is not working.
The code:
var schemaRequest = WebRequest.Create(new Uri(SchemaUri)) as HttpWebRequest;
schemaRequest.Method = "PUT";
schemaRequest.ContentType = "text/xml";
schemaRequest.Credentials = CredentialCache.DefaultNetworkCredentials;
schemaRequest.Proxy = WebRequest.DefaultWebProxy;
schemaRequest.AddRange(1024);
string test = "<ArrayOfUpdateNodeRequest> <UpdateNodeRequest> <Description>vijay</Description> <Name>Publishing</Name></UpdateNodeRequest></ArrayOfUpdateNodeRequest>";
byte[] arr = new byte[1024];
arr = System.Text.Encoding.UTF8.GetBytes(test);
schemaRequest.ContentLength = arr.Length;
using (var dataStream = schemaRequest.GetRequestStream())
{
dataStream.Write(arr, 0, arr.Length);
}
I'm getting the exception "This stream does not support seek operations." at GetRequestStream().
The exception is pretty clear, the steam doesn't support seeking. Looking at the object in the debugger does not mean you need to seek--if you do, please provide an example. You should simply be able to write to the stream for it to be sent to the host. It doesn't make sense to be able to seek when you're sending a stream to a host (e.g, how do you seek back before a byte you've already sent over the wire to the host?).
If you need to seek locally, before sending to the host, create a memory stream and seek that way. For example:
using (MemoryStream memoryStream new MemoryStream())
{
// ... writes
memoryStream.Seek(0, SeekOrigin.Begin);
//... writes
memoryStream.CopyTo(schemaRequest.GetRequestStream());
}
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();
I implemented a Client and Server model that uses Socket with thread
When I want to pass only a string from Client to the Server, it works. But I want to pass an object and it throws this error:
"Attempting to deserialize an empty stream"
Here is the code:
Client:
ASCIIEncoding asen = new ASCIIEncoding();
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
Command command = new Command("meuLogin", "minhaSenha");
binaryFormatter.Serialize(memoryStream, command);
stream.Write(memoryStream.ToArray(), 0, memoryStream.ToArray().Length);
Server:
byte[] message = new byte[4096];
int bytesRead = 0;
bytesRead = clientStream.Read(message, 0, 4096);
MemoryStream memoryStream = new MemoryStream(bytesRead);
BinaryFormatter bf1 = new BinaryFormatter();
memoryStream.Position = 0;
Command command = (Command)bf1.Deserialize(memoryStream);
Another question: I copied the class Command from Client and pasted at Server to deserialize. Is this correct?
Thank you
I also recommend WCF. But if you continue using sockets, the key element that you're missing in your protocol is message framing.
You never use the message that you read from the stream. The memory stream you are reading from is thus empty.
On a side note, why do you use these intermediate MemoryStreams?
To answer your second question: for maximum maintainability, the class Command should be in a separate assembly that both Client and Server reference.
To answer your first question: you are attempting to deserialize from an empty stream on your server, just as the exception tells you. You need to copy the bytes you read from the clientStream into the memoryStream before you deserialize from the memoryStream. Alternatively, use the clientStream directly rather than using the memoryStream; this may require reconsidering your protocol.
Finally, I wholeheartedly agree with #Andrey: consider using WCF. It's way way way better than raw sockets.
If you change your server code to use a different MemoryStream constructor, the problem will go away.
MemoryStream memoryStream = new MemoryStream(message, 0, bytesRead);
However, I agree with Stephen and others. Either use WCF, or use Message framing.
Another question: I copied the class Command from Client and pasted at Server to deserialize. Is this correct?
No. The namespace for your Client and Server are likely different, so the Server will be trying to deserialize a stream that matches its namespace.
You should create your Command Class using its own namespace and reference that from both your Client and the Server.
After that...
Client:
static void StreamToServer(TcpClient client, Command obj) {
using (NetworkStream ns = client.GetStream()) {
using (MemoryStream ms = new MemoryStream()) {
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
byte[] buf = ms.ToArray();
ns.Write(buf, 0, buf.Length);
}
}
}
Server:
static Command ReadStream(TcpListener listener) {
Command obj = null;
using (TcpClient client = listener.AcceptTcpClient()) { // waits for data
using (NetworkStream ns = client.GetStream()) {
byte[] buf = new byte[client.ReceiveBufferSize];
int len = ns.Read(buf, 0, buf.Length);
using (MemoryStream ms = new MemoryStream(buf, 0, len)) {
BinaryFormatter formatter = new BinaryFormatter();
obj = formatter.Deserialize(ms) as Command;
}
}
}
return obj;
}
WCF or Message framing may be easier, but I don't often have the opportunity at work to sit around and read a book.
Instead of passing bytesRead to the MemoryStream which is actually the length of the byte stream, you should pass 'message', as it is the actual stream of bytes. Like,
MemoryStream memoryStream = new MemoryStream(message);
As you are passing an integer variable, the compiler is throwing exception that the stream is empty.
As for WCF, it is remarkable framework, but for applications that require low latency and high performance, WCF is a horrible answer because of its overheads, it is built upon sockets. So if you use sockets, that will be the lowest level implementation and thus, the fastest. That depends on your application which paradigm you should choose...