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
- Diferencias en la visibilidad de clase interna protegida entre paquetes en diferentes versiones de Android
- ¿Por qué Java (Android?) Está estableciendo la fecha de la última modificación de mi archivo a mañana?
- FindViewById () está regresando no puede encontrar el símbolo, pero el ID se define en el diseño?
- Java.lang.NullPointerException: Intenta invocar el método virtual 'int android.view.ViewGroup.getPaddingLeft ()' en una referencia de objeto nulo
- NoClassDefFoundError en tiempo de ejecución pero la clase está en classes.dex, ¿qué givies?
- Iniciar una sesión reanudable
- Guardar el URI de la sesión reanudable
- 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?
- Cómo corregir el movimiento suave del marcador en google maps v2?
- Agregar manualmente canción a Mediastore como una pista de música
- Añadir scroll en tabhost en android
- La posición de desplazamiento salta a los elementos de la lista anterior mientras se desplaza hacia arriba en la vista de lista personalizada
- Establecer dinámicamente la autoridad de un ContentProvider
- Cómo persistir el miembro estático en las preferencias compartidas cuando está a punto de ser recogido en Android
- Obtener el número de teléfono en formato XXX-XXX-XXXX desde el cuadro edittext en android
- ¿Cómo cambiar la instantánea mostrada por la lista de aplicaciones recientes?
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!
- Consulta de MapActivity para el hospital / restaurante más cercano que no funciona
- Modificar el comportamiento de carga de Cordova Plugin para acelerar el tiempo de carga