简体   繁体   English

大量下载文件

[英]Downloading a file in multiple chunks

I'm writing a piece of code that is supposed to download a single file in 1 MB chunks. 我正在编写一段应该以1 MB的块下载单个文件的代码。 I used the same code as in this question: Download large file in small chunks in C# and converted it to VB.NET. 我使用与该问题相同的代码: 在C#中以小块下载大文件并将其转换为VB.NET。

The code, same as the previous question works fine and write the file to disk. 该代码与上一个问题相同,可以正常工作并将文件写入磁盘。 But it seems that something goes wrong in the second web request. 但是似乎在第二个Web请求中出了点问题。 For the sake of testing, I'm downloading a PNG file from https://d13yacurqjgara.cloudfront.net/users/22/screenshots/631004/attachments/53012/wallpaper-retina-cinemadisplay.png using my VB.NET code below. 为了进行测试,我使用下面的VB.NET代码从https://d13yacurqjgara.cloudfront.net/users/22/screenshots/631004/attachments/53012/wallpaper-retina-cinemadisplay.png下载PNG文件。

The downloaded PNG seems to be fine half the way and the remaining image is scrambled. 下载的PNG似乎是正确的一半,剩余的图像被打乱了。

I increased the defaultSize and chunk to 10 MB and the file downloads perfectly, but it seems that something in the For/Next loop truncates or pollutes the data. 我将defaultSizechunk增大到10 MB,并且文件下载完美,但是似乎For / Next循环中的某些内容会截断或污染数据。

Any thoughts what might be causing this to happen? 有什么想法可能导致这种情况发生吗?

Private Const defaultSize As Long = 1048576
Private chunk As Long = 1048576
Private offset As Long = 0

Private Function downloadFile(ByVal url As String, ByVal filename As String) As Boolean
    Dim size As Long = getSize(url)
    Dim blockSize As Integer = Convert.ToInt32(size / defaultSize)
    Dim remainder As Integer = Convert.ToInt32(size Mod defaultSize)

    If remainder > 0 Then
        blockSize += 1
    End If

    Dim fileStream As FileStream = File.Create("C:\mydirectory\" & filename)

    For i As Integer = 0 To blockSize - 1
        If i = blockSize - 1 Then
            chunk = remainder
        End If

        Dim req As HttpWebRequest = HttpWebRequest.Create(url)
        req.Method = WebRequestMethods.Http.Get
        req.AddRange(Convert.ToInt32(offset), Convert.ToInt32(chunk + offset))
        Dim resp As HttpWebResponse = req.GetResponse()

        Using respStream As Stream = resp.GetResponseStream
            Dim buffer(4096) As Byte
            Dim bytesRead As Integer
            Do
                bytesRead = respStream.Read(buffer, 0, 4096)
                If bytesRead > 0 Then fileStream.Write(buffer, 0, bytesRead)
            Loop While bytesRead > 0
        End Using

        offset += chunk

        resp.Close()
        resp.Dispose()
    Next

    fileStream.Close()

    Return True
End Function

Private Function getSize(ByVal url As String) As Long
    Dim req As WebRequest = WebRequest.Create(url)
    req.Method = WebRequestMethods.Http.Head
    Dim resp As WebResponse = req.GetResponse
    Return Long.Parse(resp.ContentLength)
End Function

Aside from tidying up a bit of your code (which I presume you will be doing anyway, since you converted this from C#), making one small change gets it work perfectly. 除了整理一些代码(我想您还是会做的,因为您是从C#转换过来的),做一个小的更改就可以使其完美工作。

The line 线

offset += chunk

should be 应该

offset += chunk + 1

This solves the issue because offset starts at 0, and you're adding the chunk size to it on the next loop, therefore you're adding a megabyte and starting there. 这解决了这个问题,因为offset从0开始,并且您要在下一个循环中为其添加块大小,因此您要添加一个兆字节并从那里开始。 You want to be continuing from the byte following the last megabyte! 您要从最后一个兆字节之后的字节继续! I did begin to explain how it works, but the example I came up with doesn't work... so I can't explain it! 我确实开始解释它是如何工作的,但是我想出的示例不起作用...所以我无法解释它! Maybe someone else can; 也许其他人可以; it's to do with req.AddRange - you're specifying a range, not a total. req.AddRange -您指定的是范围,而不是总计。

One issue on the code conversion. 关于代码转换的一个问题。

Convert.ToInt32(size / defaultSize)

This is the same code in the C# code and VB ignoring the semicolon. 这是C#代码和VB中的相同代码,忽略了分号。 However it does not consider the nuance that the "/" operator can return a different value with integer division in the two languages. 但是,它不考虑“ /”运算符可以用两种语言的整数除法返回不同值的细微差别。

The variable Size and defaultSize are both long integers. 变量Size和defaultSize都是长整数。 When dividing two integers in C# the result is an integer, decimal chopped and not rounded. 当在C#中将两个整数相除时,结果是一个整数,十进制小数而不是四舍五入。 In VB dividing two integers the result is coerced to decimal type then rounded. 在VB中,将两个整数相除,结果被强制为十进制类型,然后四舍五入。

/ Operator (C# Reference) /运算符(C#参考)

/ Operator (VB Reference) /运算符(VB参考)

For example if the values for size and defaultSize are 68 and 10. 例如,如果size和defaultSize的值为68和10。

VB VB

Dim result As Integer = Convert.ToInt32(size / defaultSize) 'VB result 7

C# C#

int result = Convert.ToInt32(size / defaultSize); // C# result 6

How this may affect the algorithm I have not pursued, given the original code was claimed to still not be working. 给定原始代码仍无法正常工作,这可能如何影响我尚未采用的算法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM