API Gmail позволяет загружать данные файла при создании или обновлении черновика , а также при вставке или отправке сообщения .
Параметры загрузки
API Gmail позволяет загружать определённые типы двоичных данных или медиафайлов. Конкретные характеристики загружаемых данных указаны на странице с описанием каждого метода, поддерживающего загрузку медиафайлов:
- Максимальный размер загружаемого файла : максимальный объем данных, который вы можете сохранить с помощью этого метода.
- Допустимые типы MIME медиа-данных : типы двоичных данных, которые вы можете хранить с помощью этого метода.
Вы можете отправлять запросы на загрузку любым из следующих способов. Укажите используемый метод с помощью параметра запроса uploadType
.
- Простая загрузка :
uploadType=media
. Для быстрой передачи небольших файлов, например, 5 МБ или меньше. - Многокомпонентная загрузка :
uploadType=multipart
. Для быстрой передачи небольших файлов и метаданных; файл передается вместе с описывающими его метаданными в одном запросе. - Возобновляемая загрузка :
uploadType=resumable
. Для надёжной передачи, особенно важно для больших файлов. При использовании этого метода используется запрос, инициирующий сеанс, который может включать метаданные. Это хорошая стратегия для большинства приложений, поскольку она работает и с файлами меньшего размера, но требует одного дополнительного HTTP-запроса на загрузку.
При загрузке медиафайлов используется специальный URI. Фактически, методы, поддерживающие загрузку медиафайлов, имеют два конечных URI:
URI /upload для медиа-данных. Формат конечной точки загрузки — стандартный URI ресурса с префиксом «/upload». Используйте этот URI при передаче самих медиа-данных.
Пример:
POST /upload/gmail/v1/users/ userId /messages/send
Стандартный URI ресурса для метаданных. Если ресурс содержит поля данных, эти поля используются для хранения метаданных, описывающих загруженный файл. Этот URI можно использовать при создании или обновлении значений метаданных.
Пример:
POST /gmail/v1/users/ userId /messages/send
Простая загрузка
Самый простой способ загрузить файл — это сделать простой запрос на загрузку. Этот вариант подходит, когда:
- Файл достаточно мал, чтобы его можно было загрузить заново целиком в случае сбоя соединения.
- Метаданные для отправки отсутствуют. Это может быть актуально, если вы планируете отправлять метаданные для этого ресурса в отдельном запросе или если метаданные не поддерживаются или недоступны.
Чтобы использовать простую загрузку, отправьте запрос POST
или PUT
к URI метода /upload и добавьте параметр запроса uploadType=media
. Например:
POST https://www.googleapis.com/upload/gmail/v1/users/userId/messages/send?uploadType=media
HTTP-заголовки, которые следует использовать при создании простого запроса на загрузку, включают:
-
Content-Type
. Установите один из допустимых для метода типов данных загружаемого мультимедиа, указанных в справочнике API. -
Content-Length
. Укажите количество загружаемых байтов. Не требуется, если вы используете кодирование с передачей по частям .
Пример: простая загрузка
В следующем примере показано использование простого запроса на загрузку для API Gmail.
POST /upload/gmail/v1/users/userId/messages/send?uploadType=media HTTP/1.1 Host: www.googleapis.com Content-Type: message/rfc822 Content-Length: number_of_bytes_in_file Authorization: Bearer your_auth_token Email Message data
Если запрос выполнен успешно, сервер возвращает код статуса HTTP 200 OK
вместе со всеми метаданными:
HTTP/1.1 200 Content-Type: application/json {
"id": string,
"threadId": string,
"labelIds": [
string
],
"snippet": string,
"historyId": unsigned long,
"payload": {
"partId": string,
"mimeType": string,
"filename": string,
"headers": [
{
"name": string,
"value": string
}
],
"body": users.messages.attachments Resource,
"parts": [
(MessagePart)
]
},
"sizeEstimate": integer,
"raw": bytes
}
Многокомпонентная загрузка
Если у вас есть метаданные, которые вы хотите отправить вместе с загружаемыми данными, вы можете сделать один multipart/related
запрос. Это хороший вариант, если отправляемые данные достаточно малы, чтобы их можно было загрузить полностью в случае сбоя соединения.
Чтобы использовать многокомпонентную загрузку, отправьте запрос POST
или PUT
на URI метода /upload и добавьте параметр запроса uploadType=multipart
, например:
POST https://www.googleapis.com/upload/gmail/v1/users/userId/messages/send?uploadType=multipart
HTTP-заголовки верхнего уровня, которые следует использовать при выполнении многокомпонентного запроса на загрузку, включают в себя:
-
Content-Type
. Установите значение multipart/related и включите строку границы, используемую для обозначения частей запроса. -
Content-Length
. Задаёт общее количество байтов в теле запроса. Медиа-часть запроса должна быть меньше максимального размера файла, указанного для этого метода.
Тело запроса отформатировано как тип содержимого multipart/related
[ RFC2387 ] и состоит ровно из двух частей. Части обозначены граничной строкой, за которой следуют два дефиса.
Каждой части составного запроса необходим дополнительный заголовок Content-Type
:
- Часть метаданных: должна быть первой, а
Content-Type
должен соответствовать одному из принятых форматов метаданных. - Часть мультимедиа: должна идти второй, а
Content-Type
должен соответствовать одному из принятых методов типов MIME мультимедиа.
Список допустимых типов MIME для каждого метода и ограничения по размеру загружаемых файлов см. в справочнике API.
Примечание: чтобы создать или обновить только часть метаданных, без загрузки связанных данных, просто отправьте запрос POST
или PUT
на стандартную конечную точку ресурса: https://www.googleapis.com/gmail/v1/users/ userId /messages/send
Пример: многокомпонентная загрузка
В примере ниже показан многокомпонентный запрос на загрузку для API Gmail.
POST /upload/gmail/v1/users/userId/messages/send?uploadType=multipart HTTP/1.1 Host: www.googleapis.com Authorization: Bearer your_auth_token Content-Type: multipart/related; boundary=foo_bar_baz Content-Length: number_of_bytes_in_entire_request_body --foo_bar_baz Content-Type: application/json; charset=UTF-8 {
"id": string,
"threadId": string,
"labelIds": [
string
],
"snippet": string,
"historyId": unsigned long,
"payload": {
"partId": string,
"mimeType": string,
"filename": string,
"headers": [
{
"name": string,
"value": string
}
],
"body": users.messages.attachments Resource,
"parts": [
(MessagePart)
]
},
"sizeEstimate": integer,
"raw": bytes
} --foo_bar_baz Content-Type: message/rfc822 Email Message data --foo_bar_baz--
Если запрос выполнен успешно, сервер возвращает код статуса HTTP 200 OK
вместе со всеми метаданными:
HTTP/1.1 200 Content-Type: application/json {
"id": string,
"threadId": string,
"labelIds": [
string
],
"snippet": string,
"historyId": unsigned long,
"payload": {
"partId": string,
"mimeType": string,
"filename": string,
"headers": [
{
"name": string,
"value": string
}
],
"body": users.messages.attachments Resource,
"parts": [
(MessagePart)
]
},
"sizeEstimate": integer,
"raw": bytes
}
Возобновляемая загрузка
Для более надежной загрузки файлов данных можно использовать протокол возобновляемой загрузки. Этот протокол позволяет возобновлять загрузку после прерывания потока данных из-за сбоя связи. Он особенно полезен при передаче больших файлов с высокой вероятностью сбоя сети или других сбоев передачи данных, например, при загрузке из мобильного клиентского приложения. Он также может снизить нагрузку на полосу пропускания в случае сетевых сбоев, поскольку не требует перезапуска загрузки больших файлов с самого начала.
Шаги по использованию возобновляемой загрузки включают в себя:
- Начните возобновляемый сеанс . Сделайте первоначальный запрос к URI загрузки, содержащему метаданные, если таковые имеются.
- Сохраните URI возобновляемого сеанса . Сохраните URI сеанса, полученный в ответе на первоначальный запрос; он понадобится вам для оставшихся запросов в этом сеансе.
- Загрузите файл . Отправьте медиа-файл на URI возобновляемого сеанса.
Кроме того, приложения, использующие возобновляемую загрузку, должны иметь код для возобновления прерванной загрузки . Если загрузка прерывается, выясните, сколько данных было успешно получено, а затем возобновите загрузку с этого момента.
Примечание: срок действия URI загрузки истекает через одну неделю.
Шаг 1: Начните возобновляемый сеанс
Чтобы инициировать возобновляемую загрузку, отправьте запрос POST
или PUT
на URI метода /upload и добавьте параметр запроса uploadType=resumable
, например:
POST https://www.googleapis.com/upload/gmail/v1/users/userId/messages/send?uploadType=resumable
Для этого начального запроса тело либо пусто, либо содержит только метаданные; фактическое содержимое файла, который вы хотите загрузить, будет передано в последующих запросах.
Используйте следующие заголовки HTTP с первоначальным запросом:-
X-Upload-Content-Type
. Установите MIME-тип медиаданных для загрузки, которые будут передаваться в последующих запросах. -
X-Upload-Content-Length
. Задаёт количество байт загружаемых данных, которые будут переданы в последующих запросах. Если длина неизвестна на момент запроса, этот заголовок можно опустить. - При предоставлении метаданных:
Content-Type
. Устанавливается в соответствии с типом данных метаданных. -
Content-Length
. Задаёт количество байтов, предоставленных в теле исходного запроса. Не требуется, если используется кодирование по частям .
Список допустимых типов MIME для каждого метода и ограничения по размеру загружаемых файлов см. в справочнике API.
Пример: запрос на возобновление сеанса
В следующем примере показано, как инициировать возобновляемый сеанс для API Gmail.
POST /upload/gmail/v1/users/userId/messages/send?uploadType=resumable HTTP/1.1 Host: www.googleapis.com Authorization: Bearer your_auth_token Content-Length: 38 Content-Type: application/json; charset=UTF-8 X-Upload-Content-Type: message/rfc822 X-Upload-Content-Length: 2000000 {
"id": string,
"threadId": string,
"labelIds": [
string
],
"snippet": string,
"historyId": unsigned long,
"payload": {
"partId": string,
"mimeType": string,
"filename": string,
"headers": [
{
"name": string,
"value": string
}
],
"body": users.messages.attachments Resource,
"parts": [
(MessagePart)
]
},
"sizeEstimate": integer,
"raw": bytes
}
Примечание: для первоначального возобновляемого запроса на обновление без метаданных оставьте тело запроса пустым и установите заголовок Content-Length
равным 0
.
В следующем разделе описывается, как обрабатывать ответ.
Шаг 2: Сохраните URI возобновляемого сеанса
Если запрос на инициацию сеанса выполнен успешно, API-сервер отвечает кодом статуса HTTP 200 OK
. Кроме того, он предоставляет заголовок Location
, который указывает URI возобновляемого сеанса. Заголовок Location
, показанный в примере ниже, включает часть параметра запроса upload_id
, которая содержит уникальный идентификатор загрузки для использования в этом сеансе.
Пример: ответ на инициирование возобновляемого сеанса
Вот ответ на запрос в Шаге 1:
HTTP/1.1 200 OK Location: https://www.googleapis.com/upload/gmail/v1/users/userId/messages/send?uploadType=resumable&upload_id=xa298sd_sdlkj2 Content-Length: 0
Значение заголовка Location
, как показано в примере ответа выше, — это URI сеанса, который вы будете использовать в качестве конечной точки HTTP для фактической загрузки файла или запроса статуса загрузки.
Скопируйте и сохраните URI сеанса, чтобы его можно было использовать для последующих запросов.
Шаг 3: Загрузите файл
Чтобы загрузить файл, отправьте PUT
запрос на URI загрузки, полученный на предыдущем шаге. Формат запроса на загрузку следующий:
PUT session_uri
HTTP-заголовки, используемые при создании запросов на возобновляемую загрузку файлов, включают Content-Length
. Задайте для него количество байтов, загружаемых в данном запросе, что обычно соответствует размеру загружаемого файла.
Пример: запрос на возобновление загрузки файла
Вот возобновляемый запрос на загрузку всего файла сообщения электронной почты размером 2 000 000 байт для текущего примера.
PUT https://www.googleapis.com/upload/gmail/v1/users/userId/messages/send?uploadType=resumable&upload_id=xa298sd_sdlkj2 HTTP/1.1 Content-Length: 2000000 Content-Type: message/rfc822 bytes 0-1999999
В случае успешного запроса сервер отвечает HTTP 201 Created
вместе со всеми метаданными, связанными с этим ресурсом. Если бы начальный запрос возобновляемого сеанса был PUT
-запросом на обновление существующего ресурса, ответом об успешном выполнении был бы 200 OK
вместе со всеми метаданными, связанными с этим ресурсом.
Если запрос на загрузку прерван или вы получили ответ HTTP 503 Service Unavailable
или любой другой ответ 5xx
от сервера, следуйте процедуре, описанной в разделе возобновление прерванной загрузки .
Загрузка файла по частям
Возобновляемые загрузки позволяют разбить файл на фрагменты и отправлять серию запросов для последовательной загрузки каждого фрагмента. Этот подход не является предпочтительным, поскольку дополнительные запросы приводят к снижению производительности, и, как правило, он не нужен. Тем не менее, фрагментация может потребоваться для сокращения объёма данных, передаваемых в одном запросе. Это полезно, когда для отдельных запросов установлено фиксированное время, как это актуально для некоторых классов запросов Google App Engine. Кроме того, это позволяет, например, отображать ход загрузки для устаревших браузеров, которые по умолчанию не поддерживают этот индикатор.
Возобновить прерванную загрузку
Если запрос на загрузку прерывается до получения ответа или вы получаете от сервера ответ HTTP 503 Service Unavailable
, необходимо возобновить прерванную загрузку. Для этого:
- Запросить статус. Запросите текущий статус загрузки, отправив пустой запрос
PUT
на URI загрузки. HTTP-заголовки этого запроса должны включать заголовокContent-Range
указывающий, что текущая позиция в файле неизвестна. Например, установитеContent-Range
на*/2000000
, если общая длина файла составляет 2 000 000. Если вы не знаете полный размер файла, установитеContent-Range
на*/*
.Примечание: вы можете запрашивать статус между фрагментами, а не только в случае прерывания загрузки. Это полезно, например, если вы хотите отображать индикаторы хода загрузки для устаревших браузеров.
- Получить количество загруженных байтов. Обработать ответ на запрос статуса. Сервер использует заголовок
Range
в своём ответе, чтобы указать, какие байты он уже получил. Например, заголовокRange
0-299999
указывает, что были получены первые 300 000 байт файла. - Загрузите оставшиеся данные. Теперь, когда вы знаете, где возобновить запрос, отправьте оставшиеся данные или текущий фрагмент. Обратите внимание, что в любом случае оставшиеся данные необходимо обрабатывать как отдельный фрагмент, поэтому при возобновлении загрузки необходимо отправить заголовок
Content-Range
.
Пример: возобновление прерванной загрузки
1) Запросить статус загрузки.
Следующий запрос использует заголовок Content-Range
для указания того, что текущая позиция в файле размером 2 000 000 байт неизвестна.
PUT {session_uri} HTTP/1.1 Content-Length: 0 Content-Range: bytes */2000000
2) Извлеките из ответа количество загруженных на данный момент байтов.
В ответе сервера заголовок Range
указывает на получение первых 43 байтов файла. Верхнее значение заголовка Range
определяет, с чего начать возобновление загрузки.
HTTP/1.1 308 Resume Incomplete Content-Length: 0 Range: 0-42
Примечание: статус ответа может быть 201 Created
или 200 OK
если загрузка завершена. Это может произойти, если соединение разорвалось после загрузки всех байтов, но до того, как клиент получил ответ от сервера.
3) Возобновите загрузку с того места, где она была прервана.
Следующий запрос возобновляет загрузку путем отправки оставшихся байтов файла, начиная с байта 43.
PUT {session_uri} HTTP/1.1 Content-Length: 1999957 Content-Range: bytes 43-1999999/2000000 bytes 43-1999999
Лучшие практики
При загрузке медиафайлов полезно знать некоторые рекомендации по обработке ошибок.
- Возобновите или повторите попытки загрузки, которые не удалось выполнить из-за прерываний соединения или ошибок
5xx
, включая:-
500 Internal Server Error
-
502 Bad Gateway
-
503 Service Unavailable
-
504 Gateway Timeout
-
- Используйте стратегию экспоненциальной задержки , если при возобновлении или повторной попытке отправки запросов на загрузку возникает ошибка сервера
5xx
. Эти ошибки могут возникать при перегрузке сервера. Экспоненциальная задержка может помочь устранить подобные проблемы в периоды большого количества запросов или интенсивного сетевого трафика. - Другие типы запросов не следует обрабатывать с помощью экспоненциальной задержки, но вы всё равно можете повторить несколько таких запросов. При повторных попытках таких запросов ограничьте их количество. Например, ваш код может ограничивать количество повторных попыток до сообщения об ошибке десятью или менее.
- Обрабатывайте ошибки
404 Not Found
и410 Gone
при выполнении возобновляемых загрузок, начиная всю загрузку с самого начала.
Экспоненциальный откат
Экспоненциальная задержка — стандартная стратегия обработки ошибок для сетевых приложений, при которой клиент периодически повторяет неудачный запрос в течение всё большего периода времени. Если большой объём запросов или большой сетевой трафик приводят к ошибкам сервера, экспоненциальная задержка может быть хорошей стратегией для их обработки. С другой стороны, она не подходит для обработки ошибок, не связанных с объёмом сети или временем отклика, таких как неверные учётные данные авторизации или ошибки «файл не найден».
При правильном использовании экспоненциальная задержка повышает эффективность использования полосы пропускания, сокращает количество запросов, необходимых для получения успешного ответа, и максимизирует пропускную способность запросов в параллельных средах.
Поток реализации простого экспоненциального отката выглядит следующим образом:
- Сделайте запрос к API.
- Получите ответ
HTTP 503
, который означает, что вам следует повторить запрос. - Подождите 1 секунду + случайное_число_миллисекунд и повторите запрос.
- Получите ответ
HTTP 503
, который означает, что вам следует повторить запрос. - Подождите 2 секунды + случайное_число_миллисекунд и повторите запрос.
- Получите ответ
HTTP 503
, который означает, что вам следует повторить запрос. - Подождите 4 секунды + случайное_число_миллисекунд и повторите запрос.
- Получите ответ
HTTP 503
, который означает, что вам следует повторить запрос. - Подождите 8 секунд + случайное_число_миллисекунд и повторите запрос.
- Получите ответ
HTTP 503
, который означает, что вам следует повторить запрос. - Подождите 16 секунд + случайное_число_миллисекунд и повторите запрос.
- Стоп. Сообщить об ошибке или зарегистрировать её.
В приведённом выше примере random_number_milliseconds — это случайное число миллисекунд, меньшее или равное 1000. Это необходимо, поскольку небольшая случайная задержка помогает более равномерно распределить нагрузку и избежать перегрузки сервера. Значение random_number_milliseconds необходимо переопределять после каждого ожидания.
Примечание: время ожидания всегда равно (2 ^ n) + случайное_число_миллисекунд, где n — монотонно увеличивающееся целое число, изначально определенное как 0. Целое число n увеличивается на 1 для каждой итерации (каждого запроса).
Алгоритм настроен на завершение работы, когда n равно 5. Это ограничение предотвращает бесконечное количество повторных попыток, что приводит к общей задержке около 32 секунд, прежде чем запрос будет признан «неисправимой ошибкой». Большее количество повторных попыток допустимо, особенно если идёт длительная загрузка; просто убедитесь, что задержка повтора ограничена разумным значением, например, менее одной минуты.