Рекомбинирование большого куска zip в формате GO

Я загружаю большой.zip файл параллельно с Accept-Ranges и Goroutines. Приложение отправляет несколько запросов на загрузку 10-мегабайтных фрагментов zip-файла с URL-адреса, используя свой заголовок Range.

Запросы разделяются на разные диапазоны в виде отдельных подпрограмм, а полученные данные записываются во временные файлы. Файлы называются 1, 2, 3....

package main

import (
    "bufio"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "strconv"
    "sync"
)

var wg sync.WaitGroup

func main() {
    url := "https://path/to/large/zip/file/zipfile.zip"
    res, _ := http.Head(url)
    maps := res.Header
    length, _ := strconv.Atoi(maps["Content-Length"][0]) // Get the content length from the header request
    chunks := (length / (1024 * 1024 * 10)) + 1

    // startByte and endByte determines the positions of the chunk that should be downloaded
    var startByte = 0
    var endByte = (1024 * 1024 * 10) - 1
    //body := make([][]byte, chunks)
    body := make([]io.ReadCloser, chunks)

    for i := 0; i < chunks; i++ {
        wg.Add(1)

        go func(min int, max int, i int) {
            client := &http.Client {}
            req, _ := http.NewRequest("GET", url, nil)
            rangeHeader := "bytes=" + strconv.Itoa(min) +"-" + strconv.Itoa(max)
            fmt.Println(rangeHeader)
            req.Header.Add("Range", rangeHeader)

            resp,_ := client.Do(req)
            defer resp.Body.Close()

            reader, _ := ioutil.ReadAll(resp.Body)
            body[i] = resp.Body
            ioutil.WriteFile(strconv.Itoa(i), reader, 777) // Write to the file i as a byte array

            wg.Done()
        }(startByte, endByte, i)

        startByte = endByte + 1
        endByte += 1024 * 1024 * 10
    }
    wg.Wait()

    filepath := "zipfile.zip"
    // Create the file
    _, err := os.Create(filepath)
    if err != nil {
        return
    }
    file, _ := os.OpenFile(filepath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
    if err != nil {
        log.Fatal(err)
    }


    for j := 0; j < chunks; j++ {
        newFileChunk, err := os.Open(strconv.Itoa(j))
        if err != nil {
            log.Fatal(err)
        }
        defer newFileChunk.Close()

        chunkInfo, err := newFileChunk.Stat()
        if err != nil {
            log.Fatal(err)
        }
        var chunkSize int64 = chunkInfo.Size()
        chunkBufferBytes := make([]byte, chunkSize)

        // read into chunkBufferBytes
        reader := bufio.NewReader(newFileChunk)
        _, err = reader.Read(chunkBufferBytes)
        file.Write(chunkBufferBytes)
        file.Sync() //flush to disk
        chunkBufferBytes = nil // reset or empty our buffer
    }

    //Verify file size
    filestats, err := file.Stat()
    if err != nil {
        log.Fatal(err)
        return
    }
    actualFilesize := filestats.Size()
    if actualFilesize != int64(length) {
        log.Fatal("Actual Size: ", actualFilesize, " Expected: ", length)
        return
    }

    file.Close()
}

После того, как все файлы загружены, я пытаюсь объединить их в один файл.zip. Однако, когда файлы собраны вместе, я не могу разархивировать окончательный файл, так как он кажется поврежденным.

Я хотел бы знать, что я делаю неправильно, или если есть лучший подход к этому. Заранее спасибо.

РЕДАКТИРОВАТЬ: Ниже то, что входит в консоль

bytes=0-10485759
bytes=10485760-20971519
2018/12/04 11:21:28 Actual Size: 16877828 Expected: 16877827

1 ответ

Решение

Проблема с вашими запросами диапазона

линии

   resp,_ := client.Do(req)
   defer resp.Body.Close()

сообщается go vet так как ошибка не проверена. Если вы проверите код ответа в последнем чанке, то это 416 - это неверный используемый диапазон, измените это

resp, err := client.Do(req)
if err != nil {
    panic(err)
}
if resp.StatusCode == 416 {
    fmt.Println("incorrect range")
}
defer resp.Body.Close()

Я также изменил переменную цикла на for i := 0; i < chunks-1; i++ {и изменил раздел после рутины

startByte = endByte + 1
endByte += 1024 * 1024 * 10
if startByte >= length {
    break
}
for endByte >= length {
    endByte = endByte - 1
}

и изменил переменную цикла J аналогичным образом

Эти изменения, кажется, работают для меня, но у меня нет подходящих тестовых данных, чтобы действительно проверить

Другие вопросы по тегам