Crear un paquete

Opciones de carga

La API de Android Over The Air te permite subir datos de paquetes para crear un nuevo recurso de paquete. Estos son paquetes inalámbricos que se pueden asociar con una o más configuraciones para que la actualización se entregue a los dispositivos.

Proporcionamos un objeto binario para Linux y Windows a fin de facilitar las cargas reanudables de paquetes, que puedes usar libremente en lugar de implementar los protocolos que se describen a continuación. Si deseas una integración más profunda, usa uno de los protocolos que se describen a continuación.

Para usarla, primero debes crear una cuenta de servicio y obtener un archivo de claves JSON para esa cuenta. Consulta nuestra guía para crear la cuenta aquí.
Una vez que tengas el objeto binario y el archivo de claves, puedes ejecutarlo con las opciones de línea de comandos para especificar el archivo de claves, tu implementación y el paquete que deseas subir. Usa --help para ver todas las opciones.

Protocolos de carga

Puedes realizar solicitudes de carga de cualquiera de las siguientes maneras. Especifica el método que usas con el encabezado de la solicitud X-Goog-Upload-Protocol.

  • Carga multiparte: X-Goog-Upload-Protocol: multipart. Se usa para lograr una transferencia rápida de metadatos y archivos más pequeños; se transfiere el archivo junto con metadatos que lo describen, todo en una sola solicitud.
  • Carga reanudable: X-Goog-Upload-Protocol: resumable. Se usa para una transferencia confiable; es importante en particular para archivos más grandes. Con este método, utilizas una solicitud de inicio de sesión, que puede incluir metadatos. Esta es una buena estrategia para la mayoría de las aplicaciones, ya que también funciona para archivos más pequeños al costo de una solicitud HTTP adicional por carga.

Carga multiparte

Esta es una buena opción si los datos que vas a enviar son lo suficientemente pequeños como para volver a subirlos por completo si falla la conexión.

Para usar una carga multiparte, realiza una solicitud POST al URI /upload/package y establece X-Goog-Upload-Protocol en multipart.

Entre los encabezados HTTP de nivel superior que se deben usar cuando se realiza una solicitud de carga multiparte, se incluyen los siguientes:

  • Content-Type. Se establece como multiparte/relacionado; incluye la string de límite que usas para identificar las partes de la solicitud.
  • Content-Length. Configurado como la cantidad total de bytes en el cuerpo de la solicitud.

El cuerpo de la solicitud tiene el formato de un tipo de contenido multipart/related [RFC2387] y contiene exactamente dos partes. Las partes se identifican mediante una string de límite, y la última string de límite está seguida por dos guiones.

Cada parte de la solicitud multiparte necesita un encabezado Content-Type adicional:

  1. Parte de metadatos: Debe colocarse primero, y Content-Type debe ser application/json.
  2. Parte de contenido multimedia: Debe ir en segundo lugar, y Content-Type debe ser application/zip.

Ejemplo: Carga multiparte

En el siguiente ejemplo, se muestra una solicitud de carga multiparte para la API de Android Over The Air.

POST /upload/package HTTP/1.1
Host: androidovertheair.googleapis.com
Authorization: Bearer your_auth_token
Content-Type: multipart/related; boundary=BOUNDARY
Content-Length: number_of_bytes_in_entire_request_body

--BOUNDARY
Content-Type: application/json; charset=UTF-8

{"deployment": "id", "package_title": "title" }
--BOUNDARY
Content-Type: application/zip; charset=UTF-8

Package ZIP
--BOUNDARY--

Si la solicitud se realiza correctamente, el servidor muestra el código de estado HTTP 200 OK.

HTTP/1.1 200

Una forma de lograrlo es usar curl y oauth2l. A continuación, se incluye una solicitud de ejemplo en la que se presupone que usas una clave de servicio (consulta nuestro instructivo para realizar la autorización para obtener más información).

Solicitud cURL de ejemplo
    JSON={"deployment": "id", "package_title": "title" }
    SERVICE_KEY_FILE=path to your service key json file
    curl \
    -H "$(./oauth2l header --json $SERVICE_KEY_FILE android_partner_over_the_air)" \
    -H "Host: androidovertheair.googleapis.com" \
    -H "X-Goog-Upload-Protocol: multipart" \
    -H "Content-Type: multipart/form-data" \
    -F "json=$JSON;type=application/json" \
    -F "data=@update.zip;type=application/zip" \
    androidovertheair.googleapis.com/upload/package
  

Carga reanudable

Para cargar archivos de datos de manera más confiable, puedes usar el protocolo de carga reanudable. Con este protocolo, puedes reanudar una operación de carga si una falla de comunicación interrumpe el flujo de datos. Resulta muy útil si transfieres archivos grandes y la probabilidad de una interrupción de la red o de algún otro error en la transmisión es alta, por ejemplo, cuando realizas cargas desde una app cliente para dispositivos móviles. También puede reducir el uso del ancho de banda en caso de fallas en la red, ya que no tienes que reiniciar las cargas de archivos grandes desde el principio.

Para el protocolo de carga reanudable, se usan varios comandos:

  1. Inicia una sesión reanudable. Realiza una solicitud inicial al URI de carga que incluya los metadatos y establezca una ubicación única de carga reanudable.
  2. Guarda eI URI de la sesión reanudable. Guarda el URI de la sesión que se muestra en la respuesta de la solicitud inicial; lo usarás para las solicitudes restantes de esta sesión.
  3. Sube el archivo. Envía la totalidad o parte del archivo ZIP al URI de la sesión reanudable.

Además, las aplicaciones que usan carga reanudable deben tener un código para reanudar una carga interrumpida. Si se interrumpe una carga, averigua cuántos datos se recibieron de forma correcta y, luego, reanuda la carga desde ese punto.

Nota: Los URIs de carga vencen después de 3 días.

Paso 1: Inicia una sesión reanudable

Para iniciar una carga reanudable, realiza una solicitud POST al URI /upload/package y establece X-Goog-Upload-Protocol en resumable.

Para esta solicitud de inicio, el cuerpo debe contener solo los metadatos; transferirás el contenido real del archivo que deseas subir en solicitudes posteriores.

Usa los siguientes encabezados HTTP con la solicitud inicial:

  • X-Goog-Upload-Header-Content-Type. Este es el tipo de contenido del archivo que se está subiendo y debe establecerse en application/zip.
  • X-Goog-Upload-Command. Se estableció en start
  • X-Goog-Upload-Header-Content-Length. Configurado como la cantidad de bytes de datos de carga que se transferirán en solicitudes posteriores. Si no se conoce la longitud en el momento de esta solicitud, puedes omitir este encabezado.
  • Content-Type. Este es el tipo de contenido de los metadatos y se debe establecer en application/json.
  • Content-Length. Configurado como la cantidad de bytes que se proporcionaron en el cuerpo de esta solicitud inicial.
Ejemplo: Solicitud de inicio de sesión reanudable

En el siguiente ejemplo, se muestra cómo iniciar una sesión reanudable para la API de Android Over The Air.

POST /upload/package HTTP/1.1
Host: android/over-the-air.googleapis.com
Authorization: Bearer your_auth_token
Content-Length: 38
Content-Type: application/json; charset=UTF-8
X-Goog-Upload-Command: start
X-Goog-Upload-Header-Content-Type: application/zip
X-Goog-Upload-Header-Content-Length: 2000000

{"deployment": "id", "package_title": "title" }

En la próxima sección, se describe cómo manejar la respuesta.

Paso 2: Guarda el URI de la sesión reanudable

Si la solicitud de inicio de sesión se realiza correctamente, el servidor de la API responde con un código de estado HTTP 200 OK. Además, proporciona un encabezado X-Goog-Upload-URL que especifica el URI de la sesión reanudable. El encabezado X-Goog-Upload-URL, que se muestra en el siguiente ejemplo, incluye una parte del parámetro de consulta upload_id que proporciona el ID de carga único que se debe usar en esta sesión. La respuesta también contiene un encabezado X-Goog-Upload-Status, que será active si la solicitud de carga es válida y se aceptó. Este estado puede ser final si se rechazó la carga.

Ejemplo: Respuesta de inicio de sesión reanudable

A continuación se muestra la respuesta a la solicitud presentada en el Paso 1:

HTTP/1.1 200 OK
X-Goog-Upload-Status: active
X-Goog-Upload-URL: androidovertheair.googleapis.com/?upload_id=xa298sd_sdlkj2
Content-Length: 0

El valor del encabezado X-Goog-Upload-URL, como se muestra en la respuesta de ejemplo anterior, es el URI de sesión que usarás como extremo HTTP para subir el archivo o consultar el estado de carga.

Copia y guarda este URI de sesión para poder usarlo en solicitudes posteriores.

Paso 3: carga el archivo

Para subir el archivo, envía una solicitud POST al URI de carga que obtuviste en el paso anterior. El formato de la solicitud de carga es el siguiente:

POST session_uri

Entre los encabezados HTTP que se deben usar cuando se realizan solicitudes de carga de archivos reanudables, se incluyen los siguientes:

  1. Content-Length. Configura esta opción como la cantidad de bytes que subirás en esta solicitud, que suele ser el tamaño del archivo de carga.
  2. X-Goog-Upload-Command. Configura esto como upload y finalize.
  3. X-Goog-Upload-Offset. Esto especificó el desplazamiento en el que se deben escribir los bytes. Ten en cuenta que los clientes deben subir los bytes en serie.
Ejemplo: Solicitud de carga de archivo reanudable

Esta es una solicitud reanudable para subir todo el archivo ZIP de 2,000,000 bytes del ejemplo actual.

POST /?upload_id=xa298sd_sdlkj2 HTTP/1.1
Host: androidovertheair.googleapis.com
X-Goog-Upload-Protocol: resumable
X-Goog-Upload-Command: upload, finalize
X-Goog-Upload-Offset: 0
Content-Length: 2000000
Content-Type: application/zip

bytes 0-1999999

Si la solicitud se realiza correctamente, el servidor responde con una 200 Ok HTTP.

Si se interrumpe la solicitud de carga, o si recibes una respuesta HTTP 503 Service Unavailable o cualquier otra respuesta 5xx del servidor, sigue el procedimiento que se describe en la sección Cómo reanudar una carga interrumpida.


Sube el archivo en partes

Con las cargas reanudables, puedes fragmentar un archivo y enviar diferentes solicitudes para cargar cada parte en secuencia. Este no es el enfoque preferido, ya que las solicitudes adicionales tienen costos de rendimiento asociados y, por lo general, no son necesarias. Recomendamos que los clientes suban todos los bytes restantes de la carga útil y que incluyan el comando finalize con cada comando upload.

Sin embargo, es posible que necesites usar la fragmentación para reducir la cantidad de datos que se transfieren en una sola solicitud. Además, te permite realizar acciones como proporcionar indicaciones de progreso de carga para los navegadores heredados que no son compatibles de forma predeterminada con el progreso de la carga.


Reanuda una carga interrumpida

Si una solicitud de carga se interrumpe antes de que recibas una respuesta, o si recibes una respuesta HTTP 503 Service Unavailable del servidor, debes reanudar la carga interrumpida. Para ello, siga estos pasos:

  1. Estado de la solicitud. Envía una solicitud al URI de carga con el X-Goog-Upload-Command configurado como query para consultar el estado actual de la carga.

    Nota: Puedes solicitar el estado entre partes, no solo si se interrumpe la carga. Esto es útil, por ejemplo, si quieres mostrar indicaciones de progreso de carga en navegadores heredados.

  2. Obtén la cantidad de bytes cargados. Procesa la respuesta de la consulta de estado. El servidor usa el encabezado X-Goog-Upload-Size-Received en su respuesta para especificar cuántos bytes recibió hasta el momento.
  3. Sube los datos restantes. Por último, ahora que sabes dónde reanudar la solicitud, envía los datos restantes o el fragmento actual. Ten en cuenta que, en cualquiera de los casos, debes tratar los datos restantes como fragmentos separados, por lo que debes establecer el encabezado X-Goog-Upload-Offset en el desplazamiento adecuado cuando reanudes la carga.
Ejemplo: Reanuda una carga interrumpida

1) Solicita el estado de la carga.

POST /?upload_id=xa298sd_sdlkj2 HTTP/1.1
Host: androidovertheair.googleapis.com
X-Goog-Upload-Command: query

Al igual que con todos los comandos, el cliente debe verificar el encabezado X-Goog-Upload-Status en la respuesta HTTP de un comando de consulta. Si el encabezado está presente y el valor no es active, significa que ya finalizó la carga.

2) Extrae de la respuesta la cantidad de bytes cargados hasta el momento.

En la respuesta del servidor, se usa el encabezado X-Goog-Upload-Size-Received para indicar que se recibieron los primeros 43 bytes del archivo hasta el momento.

HTTP/1.1 200 OK
X-Goog-Upload-Status: active
X-Goog-Upload-Size-Received: 42

3) Reanuda la carga desde el punto en que se detuvo.

La siguiente solicitud reanuda la carga; para ello, envía los bytes restantes del archivo a partir del byte 43.

POST /?upload_id=xa298sd_sdlkj2 HTTP/1.1
Host: androidovertheair.googleapis.com
X-Goog-Upload-Command: upload, finalize
Content-Length: 1999957
X-Goog-Upload-Offset: 43

bytes 43-1999999

Prácticas recomendadas

Cuando cargues contenido multimedia, es útil conocer algunas de las prácticas recomendadas relacionadas con el manejo de errores:

  • Reanuda o reintenta cargas que no se realizaron de forma correcta debido a interrupciones en la conexión o cualquier error 5xx, incluidos los siguientes:
    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout
  • Usa una estrategia de retirada exponencial si se muestra cualquier error de servidor 5xx cuando reanudas o reintentas solicitudes de carga. Estos errores pueden ocurrir si un servidor se está sobrecargando. La retirada exponencial puede ayudarte a aliviar este tipo de problemas durante períodos de gran volumen de solicitudes o de tráfico de red intenso.
  • No se deben manejar otros tipos de solicitudes mediante retirada exponencial, pero igual puedes reintentar varias de ellas. Cuando reintentes estas solicitudes, limita la cantidad de veces que lo haces. Por ejemplo, tu código podría limitarse a 10 reintentos o menos antes de informar un error.
  • Maneja los errores 404 Not Found cuando realices cargas reanudables. Para ello, vuelve a iniciar toda la carga desde el comienzo.

Retirada exponencial

La retirada exponencial es una estrategia estándar de manejo de errores para aplicaciones de red en la que el cliente reintenta de forma periódica una solicitud con errores durante un período creciente. Si el servidor presenta errores debido a un gran volumen de solicitudes o tráfico de red, la retirada exponencial puede ser una buena estrategia para manejar estos errores. Por el contrario, esta estrategia no se recomienda para lidiar con errores que no estén relacionados con el volumen de la red o los tiempos de respuesta, como credenciales de autorización no válidas o errores de archivos no encontrados.

Si se la utiliza de forma correcta, la retirada exponencial aumenta la eficiencia del uso del ancho de banda, reduce la cantidad de solicitudes que se requieren para obtener una respuesta correcta y maximiza la capacidad de procesamiento de las solicitudes en entornos simultáneos.

A continuación, se muestra el flujo para implementar una retirada exponencial simple:

  1. Realizar una solicitud a la API
  2. Recibir una respuesta HTTP 503, que indica que debes reintentar la solicitud
  3. Esperar 1 segundo + random_number_milliseconds y reintentar la solicitud
  4. Recibir una respuesta HTTP 503, que indica que debes reintentar la solicitud
  5. Esperar 2 segundos + random_number_milliseconds y reintentar la solicitud
  6. Recibir una respuesta HTTP 503, que indica que debes reintentar la solicitud
  7. Esperar 4 segundos + random_number_milliseconds y reintentar la solicitud
  8. Recibir una respuesta HTTP 503, que indica que debes reintentar la solicitud
  9. Esperar 8 segundos + random_number_milliseconds y reintentar la solicitud
  10. Recibir una respuesta HTTP 503, que indica que debes reintentar la solicitud
  11. Esperar 16 segundos + random_number_milliseconds y reintentar la solicitud
  12. Detenerse. Informar o registrar un error

En el flujo anterior, random_number_milliseconds es una cantidad aleatoria de milisegundos menor o igual que 1,000. Esto es necesario, ya que agregar un pequeño retraso aleatorio ayuda a distribuir la carga de manera más uniforme y a evitar la posibilidad de fallas en cascada en el servidor. El valor de random_number_milliseconds se debe volver a definir tras cada espera.

Nota: La espera siempre es (2 ^ n) + random_number_milliseconds, en la que n es un número entero, que crece de forma monotónica, definido en un inicio como 0. El número entero n aumenta de a 1 por cada iteración (cada solicitud).

El algoritmo está configurado para terminar cuando n sea 5. Este límite impide que los clientes reintenten las solicitudes de forma infinita y genera una demora total de alrededor de 32 segundos antes de que una solicitud se considere “un error irrecuperable”. Un límite de reintentos mayor es aceptable, sobre todo si hay una carga larga en progreso. Solo asegúrate de limitar la demora de reintentos a un valor razonable, por ejemplo, menos de un minuto.