locked
Stream Writer Slow RRS feed

  • Question

  • Hi ,

        I developed a HttpClientDownloader helper class to download file from webserver. Every thing is work fine but file write operation is very slow.

     public class HttpClientDownloader
        {
            private StorageFolder storageFolder;
            private StorageFile destinationFile;
    
            public async void DownloadFile(string url, StorageFolder storagelocation = null, string fileName = null, IDictionary<string, string> headers = null)
            {
    
                HttpClientHandler handler = new HttpClientHandler();
    
                handler.Proxy = WebRequest.DefaultWebProxy;
     
                HttpClient httpClient = new HttpClient(handler);
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
                
                StorageFolder storageFolder = null;
                bool isfolderExist = true;
                try
                {
                    storageFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync("Downloads");
                }
                catch (FileNotFoundException)
                {
                    isfolderExist = false;
                }
    
                if (null == storageFolder || !isfolderExist)
                {
                    storageFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Downloads");
                }
    
    
                fileName = GenerateUniqueFileName() + "winrt.png";
    
                destinationFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
                var rst = GetFileAsyncWithProgress(httpClient, request, destinationFile, new CancellationToken());
                rst.Progress = new AsyncOperationProgressHandler<StorageFile, int>((x, y) => { HandleProgress(x.Status, y); });
                await rst;
            }
    
    
            private void HandleProgress(AsyncStatus status, int progress)
            {
                Debug.WriteLine("Size # {0} Status {1}", GetFileSize(progress.ToString()), status);
            }
    
            private static IAsyncOperationWithProgress<StorageFile, int> GetFileAsyncWithProgress(HttpClient client, HttpRequestMessage request, StorageFile destinationFile, CancellationToken cancelToken)
            {
                int read = 0;
                int offset = 0;
                byte[] responseBuffer = new byte[500];
    
                var operation = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancelToken);
    
                return AsyncInfo.Run<StorageFile, int>((token, progress) =>
                    Task.Run<StorageFile>(async () =>
                    {
                        try
                        {
                            if (cancelToken != CancellationToken.None) token = cancelToken;
    
                            using (var responseStream = await operation.Result.Content.ReadAsStreamAsync())
                            {
                                using (var fileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
                                {
    
                                    using (var dataWriter = new Windows.Storage.Streams.DataWriter(fileStream.GetOutputStreamAt(0)))
                                    {
                                        //dataWriter.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;
                                        do
                                        {
                                            if (token.IsCancellationRequested)
                                            {
                                                token.ThrowIfCancellationRequested();
                                            }
    
                                            read = await responseStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
                                            offset += read;
    
                                            //await fileStream.WriteAsync(responseBuffer, 0, read);
                                           // dataWriter.WriteBytes(responseBuffer);
                                            dataWriter.WriteBytes(responseBuffer);
                                            progress.Report(offset);
                                        } while (read != 0);
    
    
                                        
                                        await dataWriter.StoreAsync();
                                        await dataWriter.FlushAsync();
                                        dataWriter.DetachStream();
                                    }
    
                                    await fileStream.FlushAsync();
                                }
                            }
    
                        }
                        catch (Exception)
                        {
                            //handle Error
                        }
    
                        return destinationFile;
                    },
                    token));
            }
    
    
    
            private static string GetFileSize(string filesize)
            {
                try
                {
                    if (string.IsNullOrEmpty(filesize)) return "0 kb";
    
                    double bytes = Convert.ToDouble(filesize);
    
                    if (bytes < 1024)
                    {
                        return bytes.ToString("0") + " " + "bytes";
                    }
                    else if (bytes > 1024 && bytes < 1048576)
                    {
                        double kb = bytes / 1024;
                        return kb.ToString("0.0") + " " + "KB";
                    }
                    else
                    {
                        double mb = bytes / 1048576;
                        return mb.ToString("0.0") + " " + "MB";
                    }
                }
                catch
                {
    
                }
                return string.Empty;
            }
    
    
            /// <summary>
            /// Method to generate a unique file name
            /// </summary>
            /// <returns></returns>
            private string GenerateUniqueFileName()
            {
                Guid guid;
                guid = Guid.NewGuid();
                string uniqueGuid = guid.ToString();
                return uniqueGuid;
            }
        }

    Calling function :

    HttpClientDownloader http = new HttpClientDownloader();
    http.DownloadFile("http://i.msdn.microsoft.com/dynimg/IC584801.png", null, null, null);

    Could you please suggest which is the best approach to stream & write file efficiently ?

    Thanks 


    Dheeraj PK http://dheerajpk.wordpress.com/

    Tuesday, October 29, 2013 3:41 PM

Answers

  • Why so many Awaits? Is this killing your UI thread? If so - throw it off your UI thread!

    It never fails to amaze me how many different ways people can come up with to do the same thing. I think your code is far to convoluted. You are downloading a file and displaying it's progress. It's very basic - but your code is not showing that.

    Have you considered using the WebClient? Or re-writing the downloader to match that of the hundred or so downloaders available on CodePlex, SourceForge, StackOverflow etc?


    Digital Forensic Software Developer
    http:\\ccs-labs.com Mark as Answer or Vote up if useful thank you!
    Volunteer Developers Required to work on free systems that can be used to reduce online child abuse and bullying! contact: dave@ccs-labs.com if you are interested.

    Friday, November 1, 2013 11:04 AM
  • For Windows 8.1 you definitely should be using Windows.Web.Http.HttpClient.  It is extremely powerful!

    Jeff Sanders (MSFT)

    @jsandersrocks - Windows Store Developer Solutions @WSDevSol
    Getting Started With Windows Azure Mobile Services development? Click here
    Getting Started With Windows Phone or Store app development? Click here
    My Team Blog: Windows Store & Phone Developer Solutions
    My Blog: Http Client Protocol Issues (and other fun stuff I support)

    Tuesday, November 5, 2013 1:09 PM
    Moderator

All replies

  • Have you tried on different machines?

    Have you inspected the HTTP traffic with a tool like fiddler to see if the network traffic is slow?  Can you post a complete repro on SkyDrive so people can investigate this further?


    Jeff Sanders (MSFT)

    @jsandersrocks - Windows Store Developer Solutions @WSDevSol
    Getting Started With Windows Azure Mobile Services development? Click here
    Getting Started With Windows Phone or Store app development? Click here
    My Team Blog: Windows Store & Phone Developer Solutions
    My Blog: Http Client Protocol Issues (and other fun stuff I support)

    Wednesday, October 30, 2013 12:15 PM
    Moderator
  • Thanks for your reply. I tried in different machine every where same issue and no Network related issues. I removed Data writer and used stream. Its  working fine now. It would be helpful if you explain what cause the stream opeartion become slow.

        using (var responseStream = await operation.Result.Content.ReadAsStreamAsync())
                            {
                                using (var fileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
                                {
                                    using (var streamWriter = fileStream.AsStreamForWrite())
                                    {
                                        do
                                        {
                                            if (token.IsCancellationRequested)
                                            {
                                                token.ThrowIfCancellationRequested();
                                            }
    
                                            read = await responseStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
                                            offset += read;
    
                                            await streamWriter.WriteAsync(responseBuffer, 0, read);
    
                                            progress.Report(new DownloadProgressEventsArgs() { TotalBytesRecieved = offset, FileName = destinationFile.Name });
    
                                        } while (read != 0);
    
                                        await streamWriter.FlushAsync();
                                        streamWriter.Dispose();
                                    }
                                }
                            }

    Thanks

     


    Dheeraj PK http://dheerajpk.wordpress.com/

    Friday, November 1, 2013 3:41 AM
  • Why so many Awaits? Is this killing your UI thread? If so - throw it off your UI thread!

    It never fails to amaze me how many different ways people can come up with to do the same thing. I think your code is far to convoluted. You are downloading a file and displaying it's progress. It's very basic - but your code is not showing that.

    Have you considered using the WebClient? Or re-writing the downloader to match that of the hundred or so downloaders available on CodePlex, SourceForge, StackOverflow etc?


    Digital Forensic Software Developer
    http:\\ccs-labs.com Mark as Answer or Vote up if useful thank you!
    Volunteer Developers Required to work on free systems that can be used to reduce online child abuse and bullying! contact: dave@ccs-labs.com if you are interested.

    Friday, November 1, 2013 11:04 AM
  • Thanks for your reply.

    Yes , Little bit but i used below code to manage that killing UI problem.

     private async void HandleProgress(AsyncStatus status, DownloadProgressEventsArgs progress)
            {
                try
                {
                    await Task.Delay(1); // UI thread
                    if (ProgressChanged != null)
                        ProgressChanged(progress);
                }
                catch
                {
                   //Log Error If Required
                }
            }


    I referred codeplex site.(http://asyncdownloader.codeplex.com/SourceControl/latest#WpfDownload/WpfDownload/Task.cs) which use the WebClient Class for download stream.
    But in windows 8 I didn't see the webclient class.

    Thanks


    Dheeraj PK http://dheerajpk.wordpress.com/

    Tuesday, November 5, 2013 3:30 AM
  • For Windows 8.1 you definitely should be using Windows.Web.Http.HttpClient.  It is extremely powerful!

    Jeff Sanders (MSFT)

    @jsandersrocks - Windows Store Developer Solutions @WSDevSol
    Getting Started With Windows Azure Mobile Services development? Click here
    Getting Started With Windows Phone or Store app development? Click here
    My Team Blog: Windows Store & Phone Developer Solutions
    My Blog: Http Client Protocol Issues (and other fun stuff I support)

    Tuesday, November 5, 2013 1:09 PM
    Moderator
  • Thanks Jeff Sanders. Am waiting for 8.1 update.

    Dheeraj PK http://dheerajpk.wordpress.com/

    Sunday, November 10, 2013 6:06 AM