Как вычислить длину содержимого многочастного файла с запросом formdata в go

В настоящее время пытаюсь создать многоэлементный запрос на загрузку для загрузки png вместе с некоторыми дополнительными параметрами тела. Сервер возвращает ответ:

Тело запроса не содержит указанное количество байтов. Получил 142 827, ожидается 146 836

Так что, очевидно, что-то не так с расчетом. Я не могу на всю жизнь понять, как на самом деле вручную рассчитать длину содержимого тела и правильно подать файл для запроса. Смотрите ниже мой код для отправки запроса:

func signUp(email, username, password, profilePath string) account {
    session := createClient()

    capToke := captchaToken(session)

    solved := solveCaptcha(capToke, "example.com")

    if solved == failed {
        panic("Solve Failed")
    }
    extraParams := map[string]string{
        "Username":             username,
        "Age":                  string(random(18, 21)),
        "Gender":               "1",
        "EmailAddress":         email,
        "Password":             password,
        "ConfirmPassword":      password,
        "g-recaptcha-response": solved,
        "Recaptcha":            "",
        "ReceiveNewsletter":    "false",
    }

    req, err := makeFileUploadRequest("https://example.com", extraParams, "ProfileImage", profilePath)
    if err != nil {
        panic(err)
    }

    req.Header.Set("Host", "example.com")
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0")
    req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
    req.Header.Set("Accept-Language", "en-US,en;q=0.5")
    req.Header.Set("Accept-Encoding", "gzip, deflate, br")
    req.Header.Set("Referer", "https://example.com")
    req.Header.Set("Connection", "keep-alive")
    req.Header.Set("Upgrade-Insecure-Requests", "1")

    client := createClient()
    resp, err := client.Do(req)
    defer resp.Body.Close()
    if err != nil {
        panic(err)
    } else {
        var bodyContent []byte
        fmt.Println(resp.StatusCode)
        fmt.Println(resp.Header)
        resp.Body.Read(bodyContent)
        resp.Body.Close()
        fmt.Println(bodyContent)
    }
    var acc account

    acc.email = email
    acc.password = password
    acc.username = username

    return acc
}

func makeFileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    fileContents, err := ioutil.ReadAll(file)
    if err != nil {
        return nil, err
    }
    fi, err := file.Stat()
    if err != nil {
        return nil, err
    }
    file.Close()

    body := new(bytes.Buffer)
    writer := multipart.NewWriter(body)

    for key, val := range params {
        _ = writer.WriteField(key, val)
    }
    err = writer.Close()
    if err != nil {
        return nil, err
    }

    part, err := writer.CreateFormFile(paramName, fi.Name())
    if err != nil {
        return nil, err
    }
    ht, _ := http.NewRequest("POST", uri, body)
    ht.Header.Set("Content-Type", writer.FormDataContentType())
    writer.Close()
    return ht, err
}           

1 ответ

Решение

Ошибка в этих строках:

ht, _ := http.NewRequest("POST", uri, body)
ht.Header.Set("Content-Type", writer.FormDataContentType())
writer.Close()

Вызов NewRequest распознает, что тело является байтом. Буфер, и устанавливает заголовок длины содержимого, используя текущий размер буфера.

Окончательная составная граница записывается в тело запроса при вызове writer.Close(). Эта граница не включена в длину контента, установленную ранее при вызове NewRequest.

Чтобы устранить проблему, закройте программу записи перед созданием запроса:

writer.Close()
ht, _ := http.NewRequest("POST", uri, body)
ht.Header.Set("Content-Type", writer.FormDataContentType())
Другие вопросы по тегам