-1

I am trying to read a file and show updates using a progress bar. I keep getting this error reading last part of the file:

System.ArgumentOutOfRangeException: 'Value of '7340032' is not valid for 'Value'. 'Value' should be between 'minimum' and 'maximum'. Parameter name: Value'

at this location

progressBar.Value = position;

this is my whole code


        private async void readFileWithProgressBar(
              string fileToRead, ProgressBar progressBar)
        {
            var fileSize = (int)(new FileInfo(fileToRead).Length);

            // Set the maximum value of the progress bar to the file size
            progressBar.Maximum = fileSize;

            // Now read the file with FileStream functions
            int position = 0;
            int blockSize = 1024 * 1024; // Read 1 megabyte at a time
            byte[] allData = new byte[fileSize]; 

            using (var fs = new FileStream(fileToRead, FileMode.Open))
            {
                var bytesLeft = fileSize;
                while (bytesLeft > 0)
                {
                    await fs.ReadAsync(
                         allData, position, Math.Min((int)bytesLeft, blockSize));

                    // Advance the read position
                    position += blockSize;

                    // Update the progress bar
                    progressBar.Value = position; //ERROR IS HERE

                    bytesLeft -= blockSize;
                }
            }
        }
   private void button2_Click(object sender, EventArgs e)
        {
            readFileWithProgressBar(@"<path>\test.txt",progressBar1);
        }

My code executes almost to the end but then all of a sudden it stops and gives that error above.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
laiakini
  • 31
  • 1
  • 1
  • 7
  • 1
    `await ReadAsync` returns an integer indicating how many bytes were read. – ProgrammingLlama Feb 18 '21 at 07:19
  • You're going to be risking file corruption if you blindly assume that a read always fills the buffer/advance the position by the buffer size each time. As John indicates, capture the number of bytes ReadAsync returns that it read and advance/work with that number of bytes according, not the whole buffer. You also don't then need to keep tabs on how many bytes left; just read until there are no more bytes read – Caius Jard Feb 18 '21 at 07:28
  • 1
    ps; you might achieve better performance using a smaller buffer (sub 16kb seems to be the typical choice) – Caius Jard Feb 18 '21 at 07:34

1 Answers1

2

The problem is that you're using the blockSize rather than the number of bytes read. Imagine you have a 15-byte file, and your blockSize is set to 10.

The iteration will occur like this:

  1. Set maximum to 15 and position to 0
  2. Read up to 10 bytes from the file.
  3. Add 10 to position (new value: 10).
  4. Set Progress = 10
  5. Read up to 10 bytes from the file. We only have 5 bytes left to read, so we'll only actually get 5 bytes.
  6. Add 10 to position (new value: 20).
  7. Set Progress = 20.

As you can see, the new Progress value (20) is greater than Maximum (15).

Fortunately, await ReadAsync provides you with the number of bytes read, so you can store this and apply it to the position. Change your code to be like this:

int bytesRead = await fs.ReadAsync(allData, position, Math.Min((int)bytesLeft, blockSize));

// Advance the read position
position += bytesRead;
progressBar.Value = position;

In addition to this kind of situation at the end of a file, some Stream implementations might read less bytes than expected for other reasons. It's sometimes important to pay attention to how many bytes you have actually read, rather than how many bytes you have asked to read.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86