Amr Ahmed
Amr Ahmed

Reputation: 23

.NET Google drive SDK resumable download

I am using the Google Drive API to download files from Google Drive, it works fine, but I want to add pause and resume functionality.

I am trying to achieve this for 3 days of researching and reading google instructions but I didn't got it to work.

The downloaded file is always corrupted

This is my code:

    Const KB As Integer = &H400
    Dim ChunkSize As Integer = 256 * KB
    Dim FileRequest = Service.Files.Get(fileid)
    FileRequest.Fields = "createdTime,id,name,originalFilename,size,mimeType"
    Dim Results = FileRequest.Execute()
    Dim Size As Long = Results.Size

    Dim client = FileRequest.Service.HttpClient
    Using file = New FileStream(FilePath, FileMode.CreateNew, FileAccess.ReadWrite)
        file.SetLength(Size)
        Dim chunks = (Size / ChunkSize) + 1

        For index As Long = 0 To chunks - 1
            Dim request = FileRequest.CreateRequest()
            Dim from = index * ChunkSize
            Dim [to] = from + ChunkSize - 1
            request.Headers.Range = New RangeHeaderValue(from, [to])
            Dim response = Await client.SendAsync(request)

        If response.StatusCode = Net.HttpStatusCode.PartialContent OrElse response.IsSuccessStatusCode Then
                Using stream = Await response.Content.ReadAsStreamAsync()
                file.Seek(from, SeekOrigin.Begin)
                    Await stream.CopyToAsync(file)
            End Using
            End If
        Next
    End Using

I also tried this code:

    Dim FileRequest = Service.Files.Get(fileid)
    FileRequest.Fields = "createdTime,id,name,originalFilename,size,mimeType"
    Dim Results = FileRequest.Execute()
    Dim FileDownloadSize As Long = Results.Size
    Const KB As Integer = &H400
    Dim ChunkSize As Integer = 256 * KB
    Dim Stream As FileStream = New FileStream(FilePath, FileMode.Create, FileAccess.ReadWrite)
    Dim Request = Service.Files.Get(fileid)
    Dim chunks = (FileDownloadSize / ChunkSize) + 1
    Dim from = 0
    Dim client = Request.Service.HttpClient

    While from < FileDownloadSize
        Dim [to] = from + ChunkSize
        Dim GetRequest = Request.CreateRequest()

        GetRequest.Headers.Range = New RangeHeaderValue(from, [to])

        Dim response = Await client.SendAsync(GetRequest)

        If (response.StatusCode = Net.HttpStatusCode.PartialContent Or response.IsSuccessStatusCode) Then
            Using respstream = Await response.Content.ReadAsStreamAsync()
                Stream.Seek(from, SeekOrigin.Begin)
                Await respstream.CopyToAsync(Stream)
            End Using
        End If
        from = [to] + 1

    End While
    Stream.Dispose()

So What i am missing !

Upvotes: 0

Views: 126

Answers (1)

Amr Ahmed
Amr Ahmed

Reputation: 23

Finally solved it myself!

I was downloading the file meta data not it's content and i wasn't reading the downloaded bytes to resume interrupted download

I'll leave it here for anyone looking for

Function getCurrRowIndex()
    Try
        Dim row = From EachRow As DataGridViewRow In DGV.Rows
                  Where EachRow.Cells("GDrive_ID").Value = Me.CurrFileID
                  Select EachRow

        Dim rowIndex = row.First.Index

        Return rowIndex
    Catch ex As Exception
        Return -1
    End Try
End Function

Async Sub DownloadFile(fileid as String , FilePath as String)
       Try
        Const KB As Integer = &H400
        Dim chunkSize = 1024 * KB
        Dim fileRequest = Service.Files.[Get](fileid)
        fileRequest.Fields = "*"
        fileRequest.SupportsAllDrives = True
        Dim fileResponse = fileRequest.Execute()
        Dim client = fileRequest.Service.HttpClient
        Dim size = fileResponse.Size
        Dim DownloadedBytesBefore As Long = 0

        If IO.File.Exists(FilePath) Then ' Check if the same file downloaded before and get the download bytes to start from it  
            Dim myFile As New FileInfo(FilePath)
            DownloadedBytesBefore = myFile.Length
        End If

        Using file = New FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite)

            If (size / 1024 / 1024) > 150 Then
                Dim chunks, from

                chunks = ((size - DownloadedBytesBefore) / chunkSize) + 1

                For index As Long = 0 To chunks - 1
                    from = DownloadedBytesBefore + (index * chunkSize)
                    Dim [to] = from + chunkSize

                    Dim range = New System.Net.Http.Headers.RangeHeaderValue(from, [to])
                    Dim request = fileRequest.CreateRequest()
                    request.Headers.Range = range
                    Dim response = Await client.SendAsync(request)

                    If response.StatusCode <> Net.HttpStatusCode.PartialContent AndAlso Not response.IsSuccessStatusCode Then
                        Console.WriteLine(response.ToString())
                        Continue For
                    End If

                    If Not IsNothing(DGV) Then
                        Dim rowIndex As Integer = getCurrRowIndex()

                        If DGV("Status", rowIndex).Value <> "Pausing" Then
                            DGV("Status", rowIndex).Value = "Downloading"
                            DGV("BytesDownloaded", rowIndex).Value = Math.Round(CDbl(from / 1024), 2) & " Kb / " & Math.Round(CDbl(size / 1024), 2) & " Kb"
                            Dim percentage As Double = (from / size * 100)
                            DGV("Progress", rowIndex).Value = Math.Round(percentage, 2) & "%"
                        Else
                            DGV("Status", rowIndex).Value = "Paused"
                            While DGV("Status", rowIndex).Value = "Paused"
                                Threading.Thread.Sleep(1000)
                            End While
                        End If
                    End If

                    Console.WriteLine($"ChunkIndex: {index}; File Size: {size}; Bytes Downloaded: {from} ({Convert.ToDecimal(from) / (1024D * 1024D)}) MB; ")
                    file.Seek(from, SeekOrigin.Begin)
                    Await fileRequest.DownloadRangeAsync(file, range)
                Next
            Else
                AddHandler fileRequest.MediaDownloader.ProgressChanged, AddressOf ProgChanged
                fileRequest.DownloadWithStatus(file)
            End If

            If Not IsNothing(DGV) Then
                Dim rowIndex As Integer = getCurrRowIndex()

                DGV("Status", rowIndex).Value = "Completed"
                DGV("Progress", rowIndex).Value = "100%"
                DGV("BytesDownloaded", rowIndex).Value = Math.Round(CDbl(size / 1024 / 1024), 2) & " Mb / " & Math.Round(CDbl(size / 1024 / 1024), 2) & " Mb"
            End If
        End Using
    Catch ex As Exception
        If Not IsNothing(DGV) Then
            Dim rowIndex As Integer = getCurrRowIndex()

            DGV("Status", rowIndex).Value = "Failed"
        End If
    End Try
End Sub

Upvotes: 2

Related Questions