Search code examples
uwpazure-mobile-servicesazure-blob-storagetemplate10storagefile

How can I successfully copy a blob stream to a file or filestream while updating progress asynchronously.?


I have been trying to update my download percent advance while downloading or uploading, uploading doesnt have a trouble at all, it works perfectly. but when I try to download a file and try to update the percent progress while waiting for it to end, as soon as I ended to download I copy the mediaStream to a Filestream an always get as a result an empty file: 0Kb.

at beginning I thought i had something wrong with my server side code, which is the one to generate my SasToken, filename and Uri.

but it works fine if I use blobStorage.DownloadToStreamAsync(fileStream) if I use that method the result is a file stored at the place I wanted with the proper extension.

so this is my testing code:

private async Task ExecuteDownloadFileCommand()
    {
        var busyView = new Busy();
        busyView.IsBusy = true;
        Busy.SetBusy(busyView.IsBusy, $"Please Wait...\nDownloading: {FileProgress * 100}");
        try
        {
            // Get the SAS token from the backend
            ICloudService cloudService = ServiceLocator.Instance.Resolve<ICloudService>();
            var storageToken = await cloudService.GetSasTokenAsync("7ec0d415c1994082a954ae6329b02915");

            // Use the SAS token to get a reference to the blob storage
            var storageUri = new Uri($"{storageToken.Uri}{storageToken.SasToken}");
            var blobStorage = new CloudBlockBlob(storageUri);

            //Organize the store file process.
            var storageFolder = await StorageFolder.GetFolderFromPathAsync(ApplicationData.Current.TemporaryFolder.Path);
            var savePicker = new Windows.Storage.Pickers.FileSavePicker();
            savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
            savePicker.FileTypeChoices.Add("Picture", new List<string> { ".jpg" });
            savePicker.SuggestedFileName = "photo.jpg";
            var storageDestinationFile = await savePicker.PickSaveFileAsync();
            FileStream fileStream = null;
            await Task.Run(() =>
            {
                fileStream = File.Create(storageDestinationFile.Path);
            });

            //await blobStorage.DownloadToStreamAsync(fileStream); //this line of code gives me the result i want.
            using (var mediaStream = await blobStorage.OpenReadAsync())
            {
                var bytesInBlocks = 1024;
                var mediaLength = mediaStream.Length;
                byte[] buffer = new byte[bytesInBlocks];
                var bytesRead = 0;
                double totalBytesRead = 0.0;
                var blocksRead = 0;
                var blockIds = new List<string>();

                IsDownloadingFile = true;
                FileProgress = 0.0;

                // Do what you need to for opening your output file
                do
                {
                    bytesRead = await mediaStream.ReadAsync(buffer, 0, bytesInBlocks);

                    if (bytesRead > 0)
                    {
                        //Update the interval counters
                        totalBytesRead += bytesRead;
                        blocksRead++;

                        //Update the progress bar.
                        var progress = totalBytesRead / mediaLength;
                        FileProgress = progress;
                        Busy.SetBusy(busyView.IsBusy, $"Please Wait... \nDownloading: {FileProgress * 100}");
                    }
                } while (bytesRead > 0);
                mediaStream.CopyTo(fileStream); //this line of code isnt copying anything.                   
                //using (var mediaRandomAccessStream = mediaStream.AsRandomAccessStream())
                //{
                //    using (var mediaInputStream = mediaRandomAccessStream.GetInputStreamAt(0)) //this line of code throws and exception because the randomaccess code cant be clone.
                //    {
                //        using (var destinationStream = await storageDestinationFile.OpenAsync(FileAccessMode.ReadWrite))
                //        {
                //            using (var destinationOutputStream = destinationStream.GetOutputStreamAt(0))
                //            {
                //                await RandomAccessStream.CopyAndCloseAsync(mediaRandomAccessStream, destinationOutputStream);
                //            }
                //        }
                //    }
                //}                    
            }
            fileStream.Dispose();
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[TaskListViewModel] Downloading error: {ex.Message}");
        }
        finally
        {
            busyView.IsBusy = false;
            IsDownloadingFile = false;
            FileProgress = 0.0;
            Busy.SetBusy(busyView.IsBusy);
        }
    }

Now I'm using UWP, also I'm using template 10 which boost my development in UWP, all i want is to be able to fully download a file while showing my end user the download progress.

but it seems I havent been able to do it just yet.

hopefully someone can point me in the right direction.


Solution

  • Well it turns out that all i needed to do was to move to the beginning of the stream like this:

     mediaStream.Seek(0, SeekOrigin.Begin);
     await mediaStream.CopyToAsync(fileStream);
    

    and it fully copies my stream to the place I set before.