Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Corrupted images when using Read and Write streams to save files

Hello fellow programmers

I'm implementing a download feature in an app using Xamarin (iOS), to get a progress on the files it is downloading.

The code I use is various examples from all around the web, showing the same principal, with the biggest difference in coding style. They all use the same ReadAsync and WriteAsync functions on System.IO.File, with a byte-array as buffer.

But no matter what example I turn to, it results in the pictures getting different artifacts on them, like this:

(Original / Downloaded)

I can't seem to find any reason why this happens. I've tried out different things to see what could trigger this. I found that changing the buffer to a larger size (say 10240) results in more artifacts, and a smaller buffer (say 1024) results in less artifacts.

When debugging different things, I found a "way" to "avoid" this from happening, but that includes adding a Task Delay right after the WriteAsync just finished, for 20 milliseconds (commented out in the code) - Not a solution I wanna go with, but tells the issue may lay in the usage of the two streams (in witch my knowledge is 'basic usage only'). I also tried to use the non-async methods, but gives the same result.

Hope someone can tell me what I did wrong here, and why.

Thanks in advance

public async Task<bool> MakeDownloadRequestAsync(string url, string destination, Action<string, long, long, float> progress = null) {
    if (_downloadHttpClient == null) {
        _downloadHttpClient = new HttpClient (new NativeMessageHandler());
    }

    bool finished = false;

    try {
        var result = await _downloadHttpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);

        long receivedBytes = 0;
        long totalBytes = result.Content.Headers.ContentLength.HasValue ? result.Content.Headers.ContentLength.Value : 0;

        System.Diagnostics.Debug.WriteLine ("Started download file: {0}", url);

        using (var stream = await _downloadHttpClient.GetStreamAsync (url))
        {
            byte[] buffer = new byte[4096];
            var filename = Path.GetFileName (url);
            var writeStream = _fileStore.OpenWrite (Path.Combine(destination, filename));

            while(true)
            {
                int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);

                if (bytesRead == 0)
                {
                    finished = true;
                    System.Diagnostics.Debug.WriteLine ("Finished downloading file: {0}", url);
                    break;
                }


                await writeStream.WriteAsync(buffer, 0, buffer.Length);
                //Task.Delay(20);
                receivedBytes += bytesRead;

                if (progress != null)
                {
                    progress.Invoke(url, receivedBytes, totalBytes, (float)receivedBytes/(float)totalBytes);
                }
            }

            writeStream.Dispose();
            stream.Dispose();
        }

    } catch (Exception e) {
        System.Diagnostics.Debug.WriteLine (e);
    }


    return finished;
}
like image 951
Pelle Ravn Avatar asked Apr 11 '16 12:04

Pelle Ravn


1 Answers

Your problem is probably this line:

await writeStream.WriteAsync(buffer, 0, buffer.Length);

It should be:

await writeStream.WriteAsync(buffer, 0, bytesRead);

Your code is writing the entire buffer...regardless of how many bytes were actually read. Instead, you should only write the number of bytes that were actually read.

like image 60
Doug Clutter Avatar answered Sep 21 '22 17:09

Doug Clutter