Загрузка вложения

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 :

  1. Часть метаданных: должна быть первой, а Content-Type должен соответствовать одному из принятых форматов метаданных.
  2. Часть мультимедиа: должна идти второй, а 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
}

Возобновляемая загрузка

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

Шаги по использованию возобновляемой загрузки включают в себя:

  1. Начните возобновляемый сеанс . Сделайте первоначальный запрос к URI загрузки, содержащему метаданные, если таковые имеются.
  2. Сохраните URI возобновляемого сеанса . Сохраните URI сеанса, полученный в ответе на первоначальный запрос; он понадобится вам для оставшихся запросов в этом сеансе.
  3. Загрузите файл . Отправьте медиа-файл на 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 , необходимо возобновить прерванную загрузку. Для этого:

  1. Запросить статус. Запросите текущий статус загрузки, отправив пустой запрос PUT на URI загрузки. HTTP-заголовки этого запроса должны включать заголовок Content-Range указывающий, что текущая позиция в файле неизвестна. Например, установите Content-Range на */2000000 , если общая длина файла составляет 2 000 000. Если вы не знаете полный размер файла, установите Content-Range на */* .

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

  2. Получить количество загруженных байтов. Обработать ответ на запрос статуса. Сервер использует заголовок Range в своём ответе, чтобы указать, какие байты он уже получил. Например, заголовок Range 0-299999 указывает, что были получены первые 300 000 байт файла.
  3. Загрузите оставшиеся данные. Теперь, когда вы знаете, где возобновить запрос, отправьте оставшиеся данные или текущий фрагмент. Обратите внимание, что в любом случае оставшиеся данные необходимо обрабатывать как отдельный фрагмент, поэтому при возобновлении загрузки необходимо отправить заголовок 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 при выполнении возобновляемых загрузок, начиная всю загрузку с самого начала.

Экспоненциальный откат

Экспоненциальная задержка — стандартная стратегия обработки ошибок для сетевых приложений, при которой клиент периодически повторяет неудачный запрос в течение всё большего периода времени. Если большой объём запросов или большой сетевой трафик приводят к ошибкам сервера, экспоненциальная задержка может быть хорошей стратегией для их обработки. С другой стороны, она не подходит для обработки ошибок, не связанных с объёмом сети или временем отклика, таких как неверные учётные данные авторизации или ошибки «файл не найден».

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

Поток реализации простого экспоненциального отката выглядит следующим образом:

  1. Сделайте запрос к API.
  2. Получите ответ HTTP 503 , который означает, что вам следует повторить запрос.
  3. Подождите 1 секунду + случайное_число_миллисекунд и повторите запрос.
  4. Получите ответ HTTP 503 , который означает, что вам следует повторить запрос.
  5. Подождите 2 секунды + случайное_число_миллисекунд и повторите запрос.
  6. Получите ответ HTTP 503 , который означает, что вам следует повторить запрос.
  7. Подождите 4 секунды + случайное_число_миллисекунд и повторите запрос.
  8. Получите ответ HTTP 503 , который означает, что вам следует повторить запрос.
  9. Подождите 8 секунд + случайное_число_миллисекунд и повторите запрос.
  10. Получите ответ HTTP 503 , который означает, что вам следует повторить запрос.
  11. Подождите 16 секунд + случайное_число_миллисекунд и повторите запрос.
  12. Стоп. Сообщить об ошибке или зарегистрировать её.

В приведённом выше примере random_number_milliseconds — это случайное число миллисекунд, меньшее или равное 1000. Это необходимо, поскольку небольшая случайная задержка помогает более равномерно распределить нагрузку и избежать перегрузки сервера. Значение random_number_milliseconds необходимо переопределять после каждого ожидания.

Примечание: время ожидания всегда равно (2 ^ n) + случайное_число_миллисекунд, где n — монотонно увеличивающееся целое число, изначально определенное как 0. Целое число n увеличивается на 1 для каждой итерации (каждого запроса).

Алгоритм настроен на завершение работы, когда n равно 5. Это ограничение предотвращает бесконечное количество повторных попыток, что приводит к общей задержке около 32 секунд, прежде чем запрос будет признан «неисправимой ошибкой». Большее количество повторных попыток допустимо, особенно если идёт длительная загрузка; просто убедитесь, что задержка повтора ограничена разумным значением, например, менее одной минуты.

Руководства по клиентской библиотеке API