Carga reanudable en Drive Rest API V3

Estoy intentando crear una sesión de subida reanudable usando la API de reposición de unidad en Android.

Según la documentación los 3 pasos que se deben seguir son

  1. Iniciar una sesión reanudable
  2. Guardar el URI de la sesión reanudable
  3. Subir el archivo

Paso 1: Utilizo el código siguiente para iniciar la sesión reanudable.

File body = new File(); body.setName(fileName); body.setMimeType(mimeType); body.setCreatedTime(modifiedDate); body.setModifiedTime(modifiedDate); body.setParents(Collections.singletonList(parentId)); HttpHeaders header = new HttpHeaders(); header.setContentLength(0L); header.setContentType("application/json; charset=UTF-8"); header.set("X-Upload-Content-Type","image/jpeg"); HttpResponse response= driveObject .files() .create(body) .setRequestHeaders(header) .set("uploadType","resumable") .buildHttpRequest() .execute(); 

Paso 2: Una vez completada la ejecución, estoy imprimiendo el encabezado de respuesta de la solicitud para ver el URI de ubicación

 System.out.println(response.getHeader().toString()); 

La salida es la siguiente

 { cache-control=[no-cache, no-store, max-age=0, must-revalidate], content-encoding=[gzip], content-type=[application/json; charset=UTF-8], date=[Thu, 06 Oct 2016 02:20:18 GMT], expires=[Mon, 01 Jan 1990 00:00:00 GMT], alt-svc=[quic=":443"; ma=2592000; v="36,35,34,33,32"], pragma=[no-cache], server=[GSE], transfer-encoding=[chunked], vary=[Origin, X-Origin], x-android-received-millis=[1475720421761], x-android-response-source=[NETWORK 200], x-android-sent-millis=[1475720420804], x-content-type-options=[nosniff], x-frame-options=[SAMEORIGIN], x-xss-protection=[1; mode=block] } 

No encuentro el URI de ubicación en el encabezado de respuesta para iniciar la carga de archivos como se especifica en la documentación ni encuentro ningún ejemplo de Java para realizar la carga que se puede reanudar.

¿Cómo recupero el URI de ubicación como se especifica en la documentación?

Yo estaba tratando de la mayor parte de una semana ahora y finalmente conseguí las cargas reanudables para ejecutar. No funciona como esperaba, pero funciona.

No utilice la API de REST de conversión para todo

Lo que he aprendido es que la API REST de Google Drive es, hasta donde yo sé, realmente incapaz de realizar subidas en bloques. Esto puede ser un error o puede ser por diseño. También puedo ser demasiado estúpido.

Pero lo que me hizo pensar fue que no podía ver ejemplos de código en ninguna parte . Todo el mundo acaba de hablar de cabeceras Http todo el tiempo. Así que esto es lo que vamos a hacer a continuación. Usaremos sólo los encabezados.

Así que aquí es cómo hacer cargas reutilizables y fragmentadas con la API REST de Google Drive y Android:

0) Inicialización

 String accountName = "account_name"; GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(context, Arrays.asList(SCOPES)).setBackOff(new ExponentialBackOff()).setSelectedAccountName(accountName); 

1) Iniciar una sesión reanudable

Siga las reglas descritas por Google en este documento :

 POST /upload/drive/v3/files?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: image/jpeg X-Upload-Content-Length: 2000000 { "name": "My File" } 

Establecer todos los campos de encabezado al igual que en el ejemplo de Google. Envíela como una solicitud POST . Utilice su variable de credential para obtener el token de autorización. El tipo MIME para X-Upload-Content-Type no es tan importante, funciona sin él también ( esta respuesta SO proporciona una función agradable para recuperarla de una ruta de acceso). Establezca X-Upload-Content-Length en la longitud total de su archivo. Establece Content-Type al formato JSON, ya que nuestro cuerpo proporcionará los metadatos para Google en el formato JSON.

Ahora cree su cuerpo de metadatos. Puse un nombre de archivo y un padre. Establezca la Content-Length a la longitud de su body en bytes. A continuación, escriba su cuerpo en el flujo de salida request.getOutputStream() .

 URL url = new URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable"); HttpURLConnection request = (HttpURLConnection) url.openConnection(); request.setRequestMethod("POST"); request.setDoInput(true); request.setDoOutput(true); request.setRequestProperty("Authorization", "Bearer " + credential.getToken()); request.setRequestProperty("X-Upload-Content-Type", getMimeType(file.getPath())); request.setRequestProperty("X-Upload-Content-Length", String.format(Locale.ENGLISH, "%d", file.length())); request.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); String body = "{\"name\": \"" + file.getName() + "\", \"parents\": [\"" + parentId + "\"]}"; request.setRequestProperty("Content-Length", String.format(Locale.ENGLISH, "%d", body.getBytes().length)); OutputStream outputStream = request.getOutputStream(); outputStream.write(body.getBytes()); outputStream.close(); request.connect(); 

2) Guardar el URI de sesión reanudable

Finalmente, connect() y espere una respuesta. Si el código de respuesta es 200 , ha iniciado con éxito una subida resumida y reanudable. Ahora guarde el URI del encabezado de location alguna parte (base de datos, archivo de texto, lo que sea). Lo vas a necesitar más tarde.

 if (request.getResponseCode() == HttpURLConnection.HTTP_OK) { String sessionUri = request.getHeaderField("location"); } 

3) Cargar el archivo

 PUT {session_uri} HTTP/1.1 Host: www.googleapis.com Content-Length: 524288 Content-Type: image/jpeg Content-Range: bytes 0-524287/2000000 bytes 0-524288 

Coloque el siguiente código en un bucle, hasta que se cargue todo el archivo. Después de cada trozo, obtendrá una respuesta con el código 308 y un encabezado de range . Desde este encabezado de range , puede leer el siguiente inicio de bloque (ver (4)).

Content-Type va a ser el tipo mime de nuevo. Content-Length es el número de bytes que carga en este fragmento. Content-Range debe ser de la forma bytes startByte-EndByte/BytesTotal . Pones esto en una solicitud PUT .

A continuación, crear un FileInputStream y establecer la posición de su byte de inicio (que obtuvo de su último cabecera de range respuesta) y leer otro trozo en su búfer. Este búfer se escribe entonces en el flujo de salida de conexión. Finalmente, connect() .

 URL url = new URL(sessionUri); HttpURLConnection request = (HttpURLConnection) url.openConnection(); request.setRequestMethod("PUT"); request.setDoOutput(true); request.setConnectTimeout(10000); request.setRequestProperty("Content-Type", getMimeType(file.getPath())); long uploadedBytes = chunkSizeInMb * 1024 * 1024; if (chunkStart + uploadedBytes > file.length()) { uploadedBytes = (int) file.length() - chunkStart; } request.setRequestProperty("Content-Length", String.format(Locale.ENGLISH, "%d", uploadedBytes)); request.setRequestProperty("Content-Range", "bytes " + chunkStart + "-" + (chunkStart + uploadedBytes - 1) + "/" + file.length()); byte[] buffer = new byte[(int) uploadedBytes]; FileInputStream fileInputStream = new FileInputStream(file); fileInputStream.getChannel().position(chunkStart); if (fileInputStream.read(buffer, 0, (int) uploadedBytes) == -1) { /* break, return, exit*/ } fileInputStream.close(); OutputStream outputStream = request.getOutputStream(); outputStream.write(buffer); outputStream.close(); request.connect(); 

4) Respuesta del Mango

Después de esto obtendrá una respuesta con el código 308 (si tiene éxito). Esta respuesta contiene un encabezado de range (mencionado).

 HTTP/1.1 308 Resume Incomplete Content-Length: 0 Range: bytes=0-524287 

Se divide esto y obtener su nuevo byte de partida de partida.

  String range = chunkUploadConnection.getHeaderField("range"); int chunkPosition = Long.parseLong(range.substring(range.lastIndexOf("-") + 1, range.length())) + 1; 

5) El código de respuesta no es 308 ?!

Puede suceder que usted consiga una respuesta 5xx . Su conexión a Internet podría fallar, el archivo podría ser borrado / cambiado de nombre durante la carga, etc. etc. No se preocupe. Siempre y cuando guarde su URI de sesión y su byte de inicio de trozo, puede reanudar la subida en cualquier momento.

Para ello, envíe un encabezado del siguiente formulario:

 PUT {session_uri} HTTP/1.1 Content-Length: 0 Content-Range: bytes */TotalFileLength URL url = new URL(sessionUri); HttpURLConnection request = (HttpURLConnection) url.openConnection(); request.setRequestMethod("PUT"); request.setDoOutput(true); request.setConnectTimeout(10000); request.setRequestProperty("Content-Length", "0"); request.setRequestProperty("Content-Range", "bytes */" + file.length()); request.connect(); 

A continuación, recibirá un 308 con una cabecera de range , desde la que puede leer el último byte subido (al igual que lo hicimos anteriormente). Toma este número y comienza a repetir el bucle.

Espero poder ayudar a algunos de ustedes. Si tiene más preguntas, solo pregunte en los comentarios y editaré la respuesta.

Si fue capaz de obtener un estado de 200 Http, proporcionará la Location como parte del encabezado. Pero por lo que vi en su System.print , no hay HttpResponse.getHeader , esto puede ser un error tipográfico y se está refiriendo a HttpResponse.getHeaders .

Si este es el caso, sugeriría primero determinar si tienes 200 OK Http código de estado, y bucle de la getAllheaders para determinar si hay un encabezado de Location enumerados.

¡Espero que esto ayude!

  • Ajuste automático del tamaño del texto para que encaje en un botón Android
  • Android Parcelable y objeto que pasa por referencia
  • ¿Cómo utilizar LibSVM en Java?
  • Android complejo método onDraw () vs diseño personalizado
  • Conjunto de conexiones SQLite
  • XmlPullParser - Analizar etiqueta analizada
  • Parse Error: Parse # enableLocalDatastore (Context) `debe invocarse antes de` Parse # initialize (Context) `
  • Cómo simular la muerte de la aplicación de Android GC
  • La imagen compartida de Android no borra la vista previa de un elemento previamente compartido
  • Cómo ver una matriz en logcat para android
  • Error de Android Studio: "El método getText () debe ser llamado desde el subproceso de interfaz de usuario, el subproceso inferido actualmente es el trabajador
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.