Search code examples
c#azureazure-storage

C# big filedownload resumable from azure blob storage


I really need some rubber ducking...

I have a file that is at least 2.3 GiB. I am currently downloading this file to a temp directory. But when the download is interrupted (connection error, or windows crash) I want the user to resume download where it stopped. And not download the whole file all over again. The code works in the fact that it continues downloading the file, but I see that the download stream is starting from the beginning again. So that means that the file ends to be (2.3 GiB + the amount bytes that were downloaded previously), which ofc corrupts my file.

I used the following snippet to resume downloading, so I hoped the stream would resume, where it stopped localStream.Seek(positionInFile, SeekOrigin.Begin);

Any ideas on what I am missing here? Here is my code.

                BlobContainerClient containerClient = new BlobContainerClient(connectionString, container);
                var blobClient = containerClient.GetBlobClient(downloadFile);

                fullOutputPath = createOutputFilePath(updateFileUri.OriginalString, outputFolder);
                downloadFileInfo = new FileInfo(fullOutputPath);

                var response = blobClient.Download(cts.Token);
                contentLength = response.Value.ContentLength;
                if (contentLength.HasValue && contentLength.Value > 0)
                {
                    if (_fileSystemService.FileExists(fullOutputPath))
                    {
                        from = downloadFileInfo.Length;
                        to = contentLength;

                        if (from == to)
                        {
                            //file is already downloaded
                            //skip it
                            progress.Report(1);
                            return;
                        }
                        fileMode = FileMode.Open;
                        positionInFile = downloadFileInfo.Length;
                    }

                    using FileStream localStream = _fileSystemService.CreateFile(fullOutputPath, fileMode, FileAccess.Write);
                    localStream.Seek(positionInFile, SeekOrigin.Begin);

                    bytesDownloaded = positionInFile;
                    double dprog = ((double)bytesDownloaded / (double)(contentLength.Value + positionInFile));
                    do
                    {
                        bytesRead = await response.Value.Content.ReadAsync(buffer, 0, buffer.Length, cts.Token);

                        await localStream.WriteAsync(buffer, 0, bytesRead, cts.Token);
                        await localStream.FlushAsync();

                        bytesDownloaded += bytesRead;
                        dprog = ((double)bytesDownloaded / (double)(contentLength.Value + positionInFile));
                        progress.Report(dprog);

                    } while (bytesRead > 0);
                }

Solution

  • I did some test for you, in my case, I use a .txt file to demo your requirement. You can see the .txt file here. As you can see, at line 151, I made an end mark: enter image description here

    I also created a local file that ends with this end mark to emulate that download is interrupted and we will continue to download from storage: enter image description here

    This is my code for fast demo below:

    static void Main(string[] args)
    {
        string containerName = "container name";
        string blobName = ".txt file name";
        string storageConnStr = "storage account conn str";
    
        string localFilePath = @"local file path";
    
        var localFileStream = new FileStream(localFilePath, FileMode.Append);
    
        var localFileLength = new FileInfo(localFilePath).Length;
    
        localFileStream.Seek(localFileLength, SeekOrigin.Begin);
    
    
        var blobServiceClient = new BlobServiceClient(storageConnStr);
        var blobClient = blobServiceClient.GetBlobContainerClient(containerName).GetBlobClient(blobName);
    
        var stream = blobClient.Download(new Azure.HttpRange(localFileLength)).Value.Content;
    
        var contentStrting = new StreamReader(stream).ReadToEnd();
    
        Console.WriteLine(contentStrting);
    
        localFileStream.Write(Encoding.ASCII.GetBytes(contentStrting));
        localFileStream.Flush();
    }
    

    Result: We only downloaded the content behind the end mark: enter image description here

    Content has been downloaded to local .txt file: enter image description here

    Pls let me know if you have any more questions.