-2

Based on the Solution presented here for the err msg I'm getting ("Data at the root level is invalid. Line 1, position 1") - much more about my travails are documented here, I tried changing my server code from this:

public async void SendInventoryXML(String userId, String pwd, String fileName)
{
    XDocument doc = XDocument.Load(await Request.Content.ReadAsStreamAsync());
    String saveLoc = String.Format(@"C:\HDP\{0}.xml", fileName);
    doc.Save(saveLoc);
}

...to this:

public async void SendInventoryXML(String userId, String pwd, String fileName)
{
    MemoryStream ms = new MemoryStream(await Request.Content.ReadAsStreamAsync());
    ms.Flush();
    ms.Position = 0;

    XDocument doc = XDocument.Load(ms);
    String saveLoc = String.Format(@"C:\HDP\{0}.xml", fileName);
    doc.Save(saveLoc);
}

...but get, "Argument 1: cannot convert from 'System.IO.Stream' to 'int'" and "The best overloaded method match for 'System.IO.MemoryStream.MemoryStream(int)' has some invalid arguments"

Why would a MemoryStream expect an int as an arg? Doesn't / shouldn't it really want an array of bytes or something like that?

Community
  • 1
  • 1
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 2
    Did you read the [documentation](http://msdn.microsoft.com/en-us/library/bx3c0489%28v=vs.110%29.aspx)? – Jesse Good Sep 04 '14 at 22:00
  • 5
    If only there were [more than one constructor](http://msdn.microsoft.com/en-us/library/System.IO.MemoryStream.MemoryStream(v=vs.110).aspx)! – Marc Gravell Sep 04 '14 at 22:02
  • 1
    Don't tag-spam. None of those relate to the compiler error (and use of API) problem that is trivially being asked about. – user2864740 Sep 04 '14 at 22:03
  • @MarcGravell But there is none that takes a Stream. (And since there is a Steam, why use a MemoryStream?) – user2864740 Sep 04 '14 at 22:04
  • 4
    Isn't the `MemoryStream` superfluous, i.e. you could load the document directly from the source stream? `MemoryStream` is useful when you have bytes that need to be treated as a stream, not when you already have a stream. Or if you need an intermediate form of storage, read the first stream into a byte array. – Tim M. Sep 04 '14 at 22:07
  • 3
    this looks like cargo cult programming, try to understand the reason for uing a memorystream in the answer to the linked question. – ths Sep 04 '14 at 22:11
  • 1
    You shouldn't be using `async void`, unless these are event handlers (which im presuming they aren't). Use `async Task` instead, and preferably `await` through the whole callchain – Yuval Itzchakov Sep 05 '14 at 00:27
  • @YuvalItzchakov: By "await through the whole callchain" do you mean: "XDocument doc = await XDocument.Load(ms);" or...??? – B. Clay Shannon-B. Crow Raven Sep 05 '14 at 03:58
  • 1
    No, i mean `await SendInventoryXML(...)` – Yuval Itzchakov Sep 05 '14 at 06:20
  • SendInventoryXML() is the Controller method ultimately called via a REST method, which is called from an ancient client that doesn't know await from a waiter. – B. Clay Shannon-B. Crow Raven Sep 06 '14 at 04:59
  • @YuvalItzchakov: I changed "void" to "Task" but it still fails. – B. Clay Shannon-B. Crow Raven Sep 08 '14 at 15:51

4 Answers4

3

MemoryStream supports a variety of constructors not just a single one requiring int:

MemoryStream()
MemoryStream(Byte[])
MemoryStream(Int32)
MemoryStream(Byte[], Boolean)
MemoryStream(Byte[], Int32, Int32)
MemoryStream(Byte[], Int32, Int32, Boolean)
MemoryStream(Byte[], Int32, Int32, Boolean, Boolean)

See MSDN for a full description of each:

http://msdn.microsoft.com/en-us/library/system.io.memorystream.memorystream(v=vs.110).aspx

However non take an existing Stream instance but you can use the method Stream.CopyTo() (and Stream.CopyToAsync() for async based code) which will copy from one stream to another, so for example:

var in_stream = await Request.Content.ReadAsStreamAsync();
var out_stream = new MemoryStream();

await in_stream.CopyToAsync(out_stream);

In your code example you can probably just get away with reading from the input stream directly, using a MemoryStream seems superfluous in this instance:

XDocument doc = XDocument.Load(in_stream);

I should also point out that you never need to call Flush() on a MemoryStream instance, the call is entirely redundant as the data is written immediately to the backing byte[] anyhow.

Lloyd
  • 29,197
  • 4
  • 84
  • 98
  • 1
    True. But none of these take a Stream and none of them help the OP as such: "cannot convert from 'System.IO.Stream' to 'int'" – user2864740 Sep 04 '14 at 22:06
2

You can't give a MemoryStream a stream as a source, if you are wanting to copy the stream in to memory then you need to create a empty Memory stream and then perform the copy.

public async void SendInventoryXML(String userId, String pwd, String fileName)
{
    var stream = await Request.Content.ReadAsStreamAsync()
    MemoryStream ms = new MemoryStream();
    await stream.CopyToAsync(ms);
    //ms.Flush(); Not needed, Flush does nothing in MemoryStream. See: http://referencesource.microsoft.com/#mscorlib/system/io/memorystream.cs
    ms.Position = 0;

    XDocument doc = XDocument.Load(ms);
    String saveLoc = String.Format(@"C:\HDP\{0}.xml", fileName);
    doc.Save(saveLoc);
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • It seems like the MemoryStream could simply be eliminated in this case as it is never written to and there is already a Stream. – user2864740 Sep 04 '14 at 22:10
  • @user2864740 yes it could, but then you would have the original code the OP posted in his first code example and he said it was not working. – Scott Chamberlain Sep 04 '14 at 22:10
  • 1
    Using a MemoryStream won't fix that error; text encoding (or data therein) is not an intrinsic part of a Stream. – user2864740 Sep 04 '14 at 22:11
  • @user2864740 No, it won't but that is not the topic of this question. This will make the code work so he can get his original error again and continue on his debugging journey on fixing is real problem ([which he already has a separate question for](http://stackoverflow.com/questions/25652692/what-is-wrong-with-this-httpwebrequest-httpwebresponse-code-why-is-it-considere)) – Scott Chamberlain Sep 04 '14 at 22:12
1

You'd better use it like this:

var httpStream = await Request.Content.ReadAsStreamAsync();
MemoryStream ms = new MemoryStream();
httpStream.CopyTo(ms);
Anton K
  • 4,658
  • 2
  • 47
  • 60
  • 1
    While this does show a solution, creating a new MemoryStream is not needed (it is mosty a useless waste of bytes as the stream is never written to) and won't fix the "other" problem. – user2864740 Sep 04 '14 at 22:13
  • You're actually right, but @b-clay-shannon needs something to start from. – Anton K Sep 04 '14 at 22:15
1

Yes, you can pass it an array of bytes. But it looks like you are passing it a Task instead. Maybe something like this (untested) code would work (copied from: Working With System Threading Tasks):

public async void SendInventoryXML(String userId, String pwd, String fileName)
{
    Task task = Request.Content.ReadAsStreamAsync().ContinueWith(t =>
    {
        var stream = t.Result;
        using (FileStream fileStream = File.Create(String.Format(@"C:\HDP\{0}.xml", fileName), (int) stream.Length)) 
        {
            byte[] bytesInStream = new byte[stream.Length];
            stream.Read(bytesInStream, 0, (int) bytesInStream.Length);
            fileStream.Write(bytesInStream, 0, bytesInStream.Length);
        }
    });
}
Community
  • 1
  • 1
Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • That's an improvement on my existing code, as it now works from Fiddler Composer (as well as from my Winforms app). Now just to get it to work (there's something wrong with the client code, I reckon) from the handheld device in .NET Prehistoric (1.1). – B. Clay Shannon-B. Crow Raven Sep 04 '14 at 23:02
  • Strike that part about it working from the Winforms app; it only works from Fiddler Composer. I still think this code is better, though - I just need to get it to work from the handheld client code. – B. Clay Shannon-B. Crow Raven Sep 04 '14 at 23:13
  • 1
    There's no need for the `Task` code if you use `await` as this method is marked as `async`. In which case he can just `await` the `ReadAsStreamAsync()` then `CopyToAsync()` to the `FileStream` instance. – Lloyd Sep 05 '14 at 08:48