using blobstore with google cloud endpoint and android

后端 未结 3 706
梦毁少年i
梦毁少年i 2020-12-04 09:16

I am developing an app-engine connected android project using the eclipse plugin. One aspect of the app is to allow user Alpha to send pictures to user Bravo. To do that I h

3条回答
  •  温柔的废话
    2020-12-04 09:36

    I used the answer of this question to build my own system that uses AppEngine Endpoints. Unlike the posts above, I want to have a clean API that directly transmits the image (as byte array) to Google Endpoint and the upload to BlobstorageService is done on the backend side. The benefit of that is that i have an atomic API. The drawback obviously the load on the server as well as the heavy marshalling operations on the client.

    Android - load, scale and serialize image and upload to endpoints

    void uploadImageBackground(Bitmap bitmap) throws IOException {
        // Important! you wanna rescale your bitmap (e.g. 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();
    }
    

    Backend API Endpoint - Upload image to 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 that handles callback from BlobstorageService

    public class BlobUploadServlet extends HttpServlet {
        @Override
        public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
            BlobstorageService blobService = BlobstoreServiceFactory.getBlobstoreService();
            List 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();
        }
    }
    

    The only thing left to do is to bind /blob/upload to UploadBlobServlet.

    Note: This doesn't seem to work when AppEngine is running locally (if executed locally, then the POST to BlobstorageService would always return a 404 NOT FOUND)

提交回复
热议问题