0

I made simple algorithm for my WinRT app. Having a stream of class webresponse. I need to download file asynchronously with "ProgressChanged" event. Here is my method:

public async void DownloadFileAsync(string uri, StorageFile path)
{
        var request = WebRequest.CreateHttp(uri);
        var response = await request.GetResponseAsync();
        if (response != null)
        {
            long totalBytes = response.ContentLength;
            long writedBytes = 0;
            using(var webStream = response.GetResponseStream())
            using (IRandomAccessStream stream = await path.OpenAsync(FileAccessMode.ReadWrite))
            using (DataWriter dataWriter = new DataWriter(stream))
            {
                await Task.Run(() =>
                {
                    int b;
                    while ((b = webStream.ReadByte()) != -1)
                    {
                        dataWriter.WriteByte((byte)b);
                        writedBytes++;
                        if (writedBytes % (totalBytes / 100) == 0)
                            OnProgressChanged(new ProgressEventArgs(writedBytes / (totalBytes / 100)));
                    }
                });
                await dataWriter.StoreAsync();
                OnDownloadComplete(new EventArgs());
            }
        }
}

ProgressEventArgs code:

public class ProgressEventArgs : EventArgs
{
    public ProgressEventArgs(double percentage)
    {
        Percentage = percentage;
    }
    public double Percentage { get; set; }
}

This is working. But I think it's should be faster. Sequential bytes reading is too slow. How can I make it faster. I'm beginner. Please help.

Noisy88
  • 181
  • 1
  • 14
  • 1
    The fact that you're a beginner doesn't matter so we don't care. maybe you just have a slow network? what exactly is slow? the download? the file reading? could you refer to a specific part/line that's slow? is the entire method going slow? – MichaelThePotato Aug 29 '16 at 07:35
  • Reading stream in while block. I think, it's not good solution. Am I wrong? – Noisy88 Aug 29 '16 at 07:39

2 Answers2

1

Reading and writing large amount of data byte by byte may slow you down, you will have millions of method calls for downloading a 1MB file. You should use a buffer instead to read data in chunks:

...
long totalBytesRead = 0;

// Allocate a 4KB buffer for each read/write    
byte[] buffer = new byte[4 * 1024];
int bytesRead;

// Read up to 4KB of data, check if any data read
while ((bytesRead = webStream.Read(buffer, 0, buffer.Length)) > 0) {
   // Write the 4KB (or less) buffer
   dataWriter.WriteBuffer(buffer.AsBuffer(0, bytesRead));
   totalBytesRead += bytesRead;

   // TODO: Report progresss
}    
....

The optimal buffer size depends on many things, a 4KB buffer should be fine. Here you can get more info.

Community
  • 1
  • 1
Mehrzad Chehraz
  • 5,092
  • 2
  • 17
  • 28
1

I reviewed your code and found the way you read bytes may cause it slow when downloading and I would like to advise you to use BackgroundDownloader class because it’s faster and can handle large resources like video,music and images.
I made a demo and hope it will be helpful to you.

Xaml part:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
   <Button Content="Download File" Click="DownloadFile"/>
</Grid>

code behind:

public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }


        private async void DownloadFile(object sender, RoutedEventArgs e)
        {
            try
            {
                Uri source = new Uri("http://h.hiphotos.baidu.com/zhidao/pic/item/6d81800a19d8bc3ed69473cb848ba61ea8d34516.jpg");
                string destination = "dog.jpg";

                StorageFile destinationFile = await KnownFolders.PicturesLibrary.CreateFileAsync(
                    destination, CreationCollisionOption.GenerateUniqueName);

                BackgroundDownloader downloader = new BackgroundDownloader();
                DownloadOperation download = downloader.CreateDownload(source, destinationFile);
                await download.StartAsync();
                // Attach progress and completion handlers.
                //HandleDownloadAsync(download, true);
                BackgroundDownloadProgress currentProgress = download.Progress;

                double percent = 100;
                if (currentProgress.TotalBytesToReceive > 0)
                {
                    percent = currentProgress.BytesReceived * 100 / currentProgress.TotalBytesToReceive;
                }

            }
           catch (Exception ex)
            {
               // LogException("Download Error", ex);
            }
        }
    }

I have tested it and it works. Also you can refer to the official sample for more information.

Best Regards