Jq Wang
Jq Wang

Reputation: 13

StreamReader.ReadLine() doesn't consume the stream

I am starting a new web application project where a user can upload a .csv file so that I can process the file.

Currently I am able to let the user upload the file but when I try to use the StreamReader to read from the stream that generated from the file, it seems that the StreamReader cannot read from the stream properly.

By the way, for the upload file part I followed the Microsoft tutorial here

Here is the code for the View.

<form method="post" enctype="multipart/form-data" asp-controller="Upload" asp-action="Upload">
<div class="form-group">
    <div class="col-md-10">
        <p>Upload one or more files using this form:</p>
        <input type="file" name="files" >
    </div>
</div>
<div class="form-group">
    <div class="col-md-10">
        <input type="submit" value="Upload" />
    </div>
</div>

Here is my code for the Controller

The len, len2 and p are variables for debugging purposes.

[HttpPost]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        if (file != null && file.Length > 0)
        {
            var filePath = Path.GetTempFileName(); //Note: May throw excepetion when temporary file exceeds 65535 per server
            var stream = new FileStream(filePath, FileMode.Create);
            await file.CopyToAsync(stream);//
            long len = stream.Length;// 412 bytes
            StreamReader reader = new StreamReader(stream);//*
            int p = reader.Peek();//currently the next character is EoF
            var val = reader.ReadLine();
            long len2 = stream.Length;// 412 bytes
            bool end = reader.EndOfStream;// true

            //do some stuff here

            return RedirectToAction("Success");
        }
        else
        {
            return RedirectToAction("Upload_fail");//file not found
        }
    }

Any advice or help will be appreciated.

Upvotes: 1

Views: 1007

Answers (1)

Kirk Larkin
Kirk Larkin

Reputation: 93173

When reading from a Stream, the current position is updated accordingly. As an example, imagine the following steps:

  1. The position starts off at 0 (the beginning of the stream).
  2. A single byte is read - This updates the position to 1.
  3. Another byte is read - This updates the position to 2.

If there were only two bytes in the stream, the position is now considered to be EOF - It is no longer possible to read additional bytes, as they've already been read.

Relating this to your example, the call to await file.CopyToAsync(stream) advances the stream's position to EOF. As such, when you wrap a StreamReader around it, there's nothing left to read. The process is the same for writing - The CopyToAsync operation is advancing both the input and the output streams, resulting in both streams sitting at EOF once the operation completes.

To complicate things a little, Streams can be forward-only, meaning that it is not possible to go backwards once data has been read. As you are working with a FileStream, I think you are able to go back to the beginning, like so:

await file.CopyToAsync(stream);
stream.Position = 0;

You could also use stream.Seek(0, SeekOrigin.Begin), as mentioned in this answer: Stream.Seek(0, SeekOrigin.Begin) or Position = 0

Upvotes: 4

Related Questions