Как Go реализует отправку файлов с точки зрения HTTP

Go

Я написал статью ранее,Быстрый запуск HTTP-запроса Go. В то время основной ссылкой на описание запросов Python было описание того, как net/http в Go инициирует HTTP-запросы.

Недавно я пытался записать это на видео,адрес. Я обнаружил, что, хотя в то время это было написано более подробно, это было только введение в использование, и я, возможно, не знаю, почему. Например, часть загрузки файла, если вы не понимаете протокол загрузки файла httpRFC 1867Так сложно узнать, почему код написан.

Сегодня, основываясь на этой теме, я расскажу, как Go реализует загрузку файлов.

Для связанных кодов, пожалуйста, посетитеhttpdemo/post. Адрес видео этой статьи:Перейти загрузить файл

Введение

Проще говоря, HTTP-загрузку файла можно разделить на три этапа, а именно организацию тела запроса, настройку Content-Type и отправку запроса Post. Запрос POST не нужно представлять, он в основном фокусируется на теле запроса и типе содержимого тела запроса.

Тело запроса, то есть тело запроса, часто используется в POST-запросах. Тело запроса не является уникальным для POST, и GET также поддерживается.Это просто соглашение, согласно которому сервер обычно игнорирует тело запроса GET.

Что такое Content-Type?

Поскольку формат тела запроса не является фиксированным, существует много возможностей.Чтобы уточнить тип содержимого тела запроса, HTTP определяет заголовок запроса Content-Type.

Общие параметры Content-Type:application/x-www-form-urlencoded(отправка формы по умолчанию),application/json(жсон),text/xml(XML формат),text/plain(Простой текст),application/octet-stream(двоичный поток) и т. д.

представить форму

Загрузку файла можно понимать как частный случай отправки формы.Во-первых, весь процесс представлен на простом примере отправки формы.

Ниже приведен текст HTTP-запроса для отправки формы.

POST http://httpbin.org/post HTTP/1.1
Content-Type: application/x-www-form-urlencoded

username=poloxue&password=123456

Тип содержимогоapplication/x-www-form-urlencoded, данные организованы по urlencoded.

Сначала используйте форму формы html для реализации. следующее:

<form method="post">
    <input type="text" name="username">
    <input type="password" name="password">
    <input type="submit">
</form>

Отправить форму формы через Post, Content-Type по умолчаниюapplication/x-www-form-urlencoded.

Код реализации в Go:

data := make(url.Values)
data.Set("username", "poloxue")
data.Set("password", "123456")

// 按 urlencoded 组织数据
body, _ := data.Encode()

// 创建请求并设置内容类型
request, _ := http.NewRequest(
    http.MethodPost,
    "http://httpbin.org/post",
    bytes.NewReader(body),
)

request.Header.Set(
    "content-type",
    "application/x-www-form-urlencoded",
)

http.DefaultClient.Do(request)

Вспомните три шага, упомянутых выше, организацию данных тела запроса, настройку Content-Type и отправку запроса.

Пакет net/htp также предоставляет более краткий способ написания, http.Post.

http.Post(
    "http://httpbin.org/post",
    "application/x-www-form-urlencoded",
    bytes.NewReader(body),
)

Загрузить файл RFC 1867

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

Если это загрузка одного файла, это можно сделать через двоичный поток тела. Но если это какие-то более сложные сценарии, такие как загрузка нескольких файлов, вам необходимо настроить протокол загрузки, и и клиент, и сервер должны обеспечить соответствующую поддержку.

Для общего требования загрузки файлов было бы лучше, если бы существовал набор стандартов. Для решения этой проблемы,RFC 1867Он родился, и его основное содержание:

  • Тип входного тега добавляет опцию файла;
  • Энктип формы увеличенmultipart/form-dataопции;

Ниже приведена форма, поддерживающая отправку файлов.

<form
    action="http://httpbin.org/post"
    method="post"
    enctype="multipart/form-data"
>
  <input type="text" name="words"/>
  <input type="file" name="uploadfile1">
  <input type="file" name="uploadfile2">
  <input type="submit">
</form>

После отправки формы вы увидите примерное содержание запроса, а именно:

POST http://httpbin.org/post HTTP/1.1
Content-Type: multipart/form-data; boundary=285fa365bd76e6378f91f09f4eae20877246bbba4d31370d3c87b752d350

multipart/form-data; boundary=285fa365bd76e6378f91f09f4eae20877246bbba4d31370d3c87b752d350
--285fa365bd76e6378f91f09f4eae20877246bbba4d31370d3c87b752d350
Content-Disposition: form-data; name="uploadFile1"; filename="uploadfile1.txt"
Content-Type: application/octet-stream

upload file1
--285fa365bd76e6378f91f09f4eae20877246bbba4d31370d3c87b752d350
Content-Disposition: form-data; name="uploadFile1"; filename="uploadfile2.txt"
Content-Type: application/octet-stream

upload file2
--285fa365bd76e6378f91f09f4eae20877246bbba4d31370d3c87b752d350
Content-Disposition: form-data; name="words"

123
--285fa365bd76e6378f91f09f4eae20877246bbba4d31370d3c87b752d350--

Примечание. Если вы используете инструменты разработчика браузера Chrome, из соображений производительности вы не можете видеть эту часть контента. Кроме того, если представление представляет собой двоичный поток, это просто куча тарабарщины, на которую нечего смотреть.

Content-Type кромеmultipart/form-data, и большеboundary=xxxСодержание.boundaryЭто означает границу, которая эквивалентнаapplication/x-www-form-urlencodedв пути&, используемый для разделения разных полей ввода.boundaryПричина такой сложности в том, что в общем текстовом содержании используется&можно разделить, но если это файловый поток,&Может конфликтовать с контентом, да и уникальность границы выше.

multipart/form-dataПодробный формат содержания не представлен. Продолжаем рассказывать о том, как реализовать эту фичу в Go.

Код реализации Go

Как реализовать загрузку файлов с помощью Go?

Основная логика по-прежнему состоит из трех шагов организации данных, установки Content-Type и отправки запросов. Но организация этой части данных намного сложнее, чем урленкодированная форма формы.

Здесь отражается простота Go, потому что стандартная библиотекаmime/multipartБыл предоставлен очень полезный метод без необходимости вручную организовывать его самостоятельно.

Предположим, теперь нужно реализовать функцию предыдущей формы form, то есть отправить два файла, uploadfile1, uploadfile2 и поле words.

Во-первых, создайтеbyte.BufferТипы переменных,body, поверх него создайтеmultipart.Writer,использовать этоwriterДанные, которые предоставит организация. код показывает, как показано ниже:

bodyBuf := &bytes.Buffer{}
writer := multipart.NewWriter(payloadBuf)

Содержимое файла организовано в первую очередь. Логика организации двух файлов одинакова. Давайте возьмем uploadfile1 в качестве примера для ознакомления. существуетwriterсоздатьfileWriterДля записи содержимого файла UploadFile1,

fileWriter, err := writer.CreateFormFile("uploadFile1", filename)

Откройте файл, который вы хотите загрузить, uploadfile1, скопируйте содержимое файла вfileWriter, следующее:

f, err := os.Open("uploadfile1")
    ...
io.Copy(fileWriter, f)

Добавлять поля очень просто, при условии установкиwordsДля 123 код выглядит следующим образом:

writer.WriteField("words", "123")

Обязательно не забудьте выключить, когда все настроеноWriter, в противном случае в тексте запроса отсутствует конечная граница.

writer.Close()

Организация данных завершена.

Впоследствии, пока данные предоставляютсяhttp.PostДостаточно.

r, err := http.Post(
    "http://httpbin.org/post",
    writer.FormDataContentType(),
    body,
)

Заполненная отправка формы, поддерживающая загрузку файла.

Суммировать

В этой статье в основном рассказывается, как использовать Go для реализации загрузки файлов, которая, по сути, представляет собой тело запроса, которое упорядочивает отправленный файл. Для четкого понимания процесса организации тела запроса необходимо знать соответствующий HTTP-протокол.rfc 1867.