Usando blobstore con el extremo de la nube de google y android

Estoy desarrollando un proyecto androide conectado a la aplicación que utiliza el complemento eclipse. Un aspecto de la aplicación es permitir que el usuario Alpha envíe imágenes al usuario Bravo. Para ello tengo la siguiente configuración:

Publicación del usuario Alpha:

  • Enviar la imagen a mi servidor de aplicaciones a través de puntos finales
  • El servidor almacena la imagen en la tienda de blob
  • Servidor almacena blobkey en el almacén de datos

Usuario Bravo obteniendo:

  • Servidor obtiene blobkey de almacén de datos
  • Servidor obtiene la imagen utilizando la clave de blob
  • Servidor envía imagen a la aplicación android utilizando puntos finales

Esta configuración tarda más de dos (2) minutos desde cuando mi aplicación android envía una imagen a cuando puedo verla en la llaga blob. Huelga decir que esto es completamente inaceptable.

Mi servidor está procesando la imagen mediante programación, a través del siguiente código:

public static BlobKey toBlobstore(Blob imageData) throws FileNotFoundException, FinalizationException, LockException, IOException { if (null == imageData) return null; // Get a file service FileService fileService = FileServiceFactory.getFileService(); // Create a new Blob file with mime-type "image/png" AppEngineFile file = fileService.createNewBlobFile("image/jpeg");// png // Open a channel to write to it boolean lock = true; FileWriteChannel writeChannel = fileService.openWriteChannel(file, lock); // This time we write to the channel directly writeChannel.write(ByteBuffer.wrap (imageData.getBytes())); // Now finalize writeChannel.closeFinally(); return fileService.getBlobKey(file); } 

¿Alguien sabe cómo puedo adaptar el ejemplo oficial para utilizar puntos finales (en el caso de que deba usar mis instancias de aplicación de motor) o utilizar getServingUrl (omitiendo mis instancias) para almacenar y servir mis blobs?
Por favor, en lugar de palabras, incluya el código. Gracias.

Voy a compartir cómo estoy haciendo esto. No estoy usando los puntos finales de google-cloud, pero sólo mi api basado en el resto, pero debe ser la misma idea de cualquier manera.

Lo pondré paso a paso con el código, espero que sea claro. Simplemente adaptaría la forma en que envía sus solicitudes para utilizar puntos finales en lugar de hacerlo más genérico como en este ejemplo. Estoy incluyendo algunos boilerplate, pero excluyendo try / catch, etc verificación de errores para la brevedad.

Paso 1 (cliente)

El primer cliente solicita una url de carga desde el servidor:

 HttpClient httpclient = new DefaultHttpClient(); HttpConnectionParams.setConnectionTimeout(httpclient.getParams(), 10000); //Timeout Limit HttpGet httpGet = new HttpGet("http://example.com/blob/getuploadurl"); response = httpclient.execute(httpGet); 

Paso 2 (servidor)

En el servidor, el servlet de solicitud de carga se vería así:

 String blobUploadUrl = blobstoreService.createUploadUrl("/blob/upload"); res.setStatus(HttpServletResponse.SC_OK); res.setContentType("text/plain"); PrintWriter out = res.getWriter(); out.print(blobUploadUrl); out.flush(); out.close(); 

Anote el argumento a createUploadUrl. Aquí es donde el cliente será redirigido una vez que la carga real se ha completado. Ahí es donde se encargará de almacenar el blobkey y / o servir url y devolverlo al cliente. Tendrás que asignar un servlet a esa url, que manejará el paso 4

Paso 3 (cliente) Volver al cliente de nuevo para enviar el archivo real a la url de carga utilizando la url devuelta desde el paso 2.

 HttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost(uploadUrlReturnedFromStep2); FileBody fileBody = new FileBody(thumbnailFile); MultipartEntity reqEntity = new MultipartEntity(); reqEntity.addPart("file", fileBody); httppost.setEntity(reqEntity); HttpResponse response = httpclient.execute(httppost) 

Una vez que esta solicitud se envía al servlet en el paso 2, será redirigida al servlet especificado en createUploadUrl() anterior

Paso 4 (servidor)

Volver al lado del servidor: Este es el servlet que maneja la url asignada a blob/upload . Aquí regresaremos el blobkey y el url de servicio al cliente en un objeto json:

 List<BlobKey> blobs = blobstoreService.getUploads(req).get("file"); BlobKey blobKey = blobs.get(0); ImagesService imagesService = ImagesServiceFactory.getImagesService(); ServingUrlOptions servingOptions = ServingUrlOptions.Builder.withBlobKey(blobKey); String servingUrl = imagesService.getServingUrl(servingOptions); res.setStatus(HttpServletResponse.SC_OK); res.setContentType("application/json"); JSONObject json = new JSONObject(); json.put("servingUrl", servingUrl); json.put("blobKey", blobKey.getKeyString()); PrintWriter out = res.getWriter(); out.print(json.toString()); out.flush(); out.close(); 

Paso 5 (cliente)

Obtendremos la URL de blobkey y de servicio del json y luego la enviaremos junto con la ID de usuario, etc., para almacenarla en la entidad del almacén de datos.

 JSONObject resultJson = new JSONObject(resultJsonString); String blobKey = resultJson.getString("blobKey"); String servingUrl = resultJson.getString("servingUrl"); List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); nameValuePairs.add(new BasicNameValuePair("userId", userId)); nameValuePairs.add(new BasicNameValuePair("blobKey",blobKey)); nameValuePairs.add(new BasicNameValuePair("servingUrl",servingUrl)); HttpClient httpclient = new DefaultHttpClient(); HttpConnectionParams.setConnectionTimeout(httpclient.getParams(), 10000); HttpPost httppost = new HttpPost(url); httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpResponse response = httpclient.execute(httppost); // Continue to store the (immediately available) serving url in local storage f.ex 

Paso 6 (servidor) Realmente almacenar todo en el almacén de datos (usando objetify en este ejemplo)

 final String userId = req.getParameter("userId"); final String blobKey = req.getParameter("blobKey"); final String servingUrl = req.getParameter("servingUrl"); ExampleEntity entity = new ExampleEntity(); entity.setUserId(userId); entity.setBlobKey(blobKey); entity.setServingUrl(servingUrl); ofy().save().entity(entity); 

Espero que esto haga las cosas más claras. Si alguien quiere editar la respuesta para usar puntos finales de la nube en lugar de este ejemplo más genérico, siéntase libre 🙂

Acerca de la url de servicio

La url de servicio es una gran manera de servir imágenes a sus clientes, debido a la forma en que puede escalar dinámicamente imágenes sobre la marcha. Por ejemplo, puede enviar imágenes más pequeñas a sus usuarios de LDPI simplemente agregando =sXXX al final de la url de servicio. Donde XXX es el tamaño de píxel de la dimensión más grande de la imagen. Usted evita completamente sus instancias y sólo paga por el ancho de banda, y el usuario sólo descarga lo que necesita.

¡PD!

Debería ser posible detenerse en el paso 4 y almacenarlo directamente allí, pasando a lo largo de userId f.ex en el paso 3. Cualquier parámetro se supone que se enviará a lo largo del paso 4, pero no conseguí que funcione, por lo que este Es cómo lo hago en este momento, así que lo estoy compartiendo de esta manera ya que sé que funciona.

He utilizado la respuesta de esta pregunta para construir mi propio sistema que utiliza AppEngine Endpoints. A diferencia de los mensajes anteriores, quiero tener una API limpia que transmita directamente la imagen (como matriz de bytes) a Google Endpoint y la subida a BlobstorageService se realiza en el lado del servidor. El beneficio de eso es que tengo una API atómica. El inconveniente, obviamente, la carga en el servidor, así como las pesadas operaciones de ordenación en el cliente.

Android: carga, escala y serializa la imagen y sube a los puntos finales

 void uploadImageBackground(Bitmap bitmap) throws IOException { // Important! you wanna rescale your bitmap (eg with Bitmap.createScaledBitmap) // as with full-size pictures the base64 representation would not fit in memory // encode bitmap into byte array (very resource-wasteful!) ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); byte[] byteArray = stream.toByteArray(); bitmap.recycle(); bitmap = null; stream = null; // Note: We encode ourselves, instead of using image.encodeImageData, as this would throw // an 'Illegal character '_' in base64 content' exception // See: http://stackoverflow.com/questions/22029170/upload-photos-from-android-app-to-google-cloud-storage-app-engine-illegal-char String base64 = Base64.encodeToString(byteArray, Base64.DEFAULT); byteArray = null; // Upload via AppEngine Endpoint (ImageUploadRequest is a generated model) ImageUploadRequest image = new ImageUploadRequest(); image.setImageData(base64); image.setFileName("picture.png"); image.setMimeType("image/png"); App.getMyApi().setImage(image).execute(); } 

Endend API Endpoint – Subir imagen a BlobstorageService

 @ApiMethod( name = "setImage", path = "setImage", httpMethod = ApiMethod.HttpMethod.POST ) public void saveFoodImageForUser(ImageUploadRequest imageRequest) throws IOException { assertNotEmpty(userId, "userId"); assertNotNull(imageRequest, "imageRequest"); // create blob url BlobstorageService blobService = BlobstoreServiceFactory.getBlobstoreService(); String uploadUrl = blobService.createUploadUrl("/blob/upload"); // create multipart body containing file HttpEntity requestEntity = MultipartEntityBuilder.create() .addBinaryBody("file", imageRequest.getImageData(), ContentType.create(imageRequest.getMimeType()), imageRequest.getFileName()) .build(); // Post request to BlobstorageService // Note: We cannot use Apache HttpClient, since AppEngine only supports Url-Fetch // See: https://cloud.google.com/appengine/docs/java/sockets/ URL url = new URL(uploadUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.addRequestProperty("Content-length", requestEntity.getContentLength() + ""); connection.addRequestProperty(requestEntity.getContentType().getName(), requestEntity.getContentType().getValue()); requestEntity.writeTo(connection.getOutputStream()); // BlobstorageService will forward to /blob/upload, which returns our json String responseBody = IOUtils.toString(connection.getInputStream()); if(connection.getResponseCode() < 200 || connection.getResponseCode() >= 400) { throw new IOException("HTTP Status " + connection.getResponseCode() + ": " + connection.getHeaderFields() + "\n" + responseBody); } // parse BlopUploadServlet's Json response ImageUploadResponse response = new Gson().fromJson(responseBody, ImageUploadResponse.class); // save blobkey and serving url ... } 

Servlet que controla la devolución de llamada de BlobstorageService

 public class BlobUploadServlet extends HttpServlet { @Override public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { BlobstorageService blobService = BlobstoreServiceFactory.getBlobstoreService(); List<BlobKey> blobs = blobService.getUploads(req).get("file"); if(blobs == null || blobs.isEmpty()) throw new IllegalArgumentException("No blobs given"); BlobKey blobKey = blobs.get(0); ImagesService imagesService = ImagesServiceFactory.getImagesService(); ServingUrlOptions servingOptions = ServingUrlOptions.Builder.withBlobKey(blobKey); String servingUrl = imagesService.getServingUrl(servingOptions); res.setStatus(HttpServletResponse.SC_OK); res.setContentType("application/json"); // send simple json response (ImageUploadResponse is a POJO) ImageUploadResponse result = new ImageUploadResponse(); result.setBlobKey(blobKey.getKeyString()); result.setServingUrl(servingUrl); PrintWriter out = res.getWriter(); out.print(new Gson().toJson(result)); out.flush(); out.close(); } } 

Lo único que queda por hacer es enlazar /blob/upload a UploadBlobServlet.

Nota : Esto no parece funcionar cuando AppEngine se ejecuta localmente (si se ejecuta localmente, entonces el POST a BlobstorageService siempre devolverá un 404 NOT FOUND)

Puesto que intenté con mucha manera de hacer el servicio de devolución de llamada en la api del punto final, aborto ese aproach. Sin embargo, podría resolver ese problema haciendo un servlet paralelo al api punto final, sólo necesita definir el servidor de la clase y agregarlo web.xml configuración. Aquí mi solución:

1 Enpoint Service para obtener la URL para cargar: Entonces el servicio coudl puede ser protegido con clientId

 @ApiMethod(name = "getUploadURL", httpMethod = HttpMethod.GET) public Debug getUploadURL() { String blobUploadUrl = blobstoreService.createUploadUrl("/update"); Debug debug = new Debug(); debug.setData(blobUploadUrl); return debug; } 

2. Ahora el cliente puede llamar al punto final para obtener la URL de subida:
Tal vez algunos como este (para el uso de Android que la biblioteca del cliente enpoint demasiado):

 gapi.client.debugendpoint.getUploadURL().execute(); 

3. El siguiente paso es hacer un post a url atrapado en el último paso: Puedes hacerlo con un httpClient de android, nuevamente, en mi caso necesito subir desde una web luego utilizo un formulario y onChangeFile () evento de devolución de llamada Obtener el uploadurl (utilizando el paso 3), entonces cuando se responde a cambiar los parámetros de formulario "acción" y "codeId" antes de que alguien decida haga clic en enviar botón:

 <form id="submitForm" action="put_here_uploadUrl" method="post" enctype="multipart/form-data"> <input type="file" name="image" onchange="onChangeFile()"> <input type="text" name="codeId" value='put_here_some_dataId'> <input type="submit" value="Submit"></form> 

4 Por último, la clase de servlet paralele:

 @SuppressWarnings("serial") public class Update extends HttpServlet{ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String userId = req.getParameter("codeId"); List<BlobKey> blobs = BSF.getService().getUploads(req).get("image"); BlobKey blobKey = blobs.get(0); ImagesService imagesService = ImagesServiceFactory.getImagesService(); ServingUrlOptions servingOptions = ServingUrlOptions.Builder.withBlobKey(blobKey); String servingUrl = imagesService.getServingUrl(servingOptions); resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("application/json"); JSONObject json = new JSONObject(); try { json.put("imageUrl", servingUrl); json.put("codeId", "picture_of_"+userId); json.put("blobKey", blobKey.getKeyString()); } catch (JSONException e){ e.printStackTrace(); } PrintWriter out = resp.getWriter(); out.print(json.toString()); out.flush(); out.close(); } } 

Y agregue a web.xml, donde com.apppack es el paquete de Update Class

 <servlet> <servlet-name>update</servlet-name> <servlet-class>com.apppack.Update</servlet-class> </servlet> <servlet-mapping> <servlet-name>update</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> 
  • Puntos finales de Google Cloud: verifyToken: La longitud de la firma no es correcta
  • Motor de aplicación de Android +: user.getUserID () es nulo en el punto final
  • Autenticación de su cliente en Puntos de extremo de Cloud sin una cuenta de Google
  • Error: Error de ejecución para la tarea ': backend: appengineEndpointsGetClientLibs
  • ¿Por qué el código de carga de blobstore tiene url de redireccionamiento?
  • Nodos de la nube de Appengine generación de cliente que no genera el archivo jar de origen
  • Google Cloud Endpoint continúa lanzando una excepción de "final inesperado"
  • Autenticación personalizada en Google Cloud Mobile Backend Starter en Android
  • Variedad de errores en la aplicación Mobile Backend Starter Sample
  • Error: Error al encontrar: com.google.guava: guava: 18.0. +
  • Uso de la versión personalizada de la aplicación de App Engine en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.