Store image to Blobstore from android client and retrieve blobkey and upload url to store in Datastore. - GAE
Asked Answered
W

3

11

In my Android application, I want to upload image to the Blobstore, then retrieve an Upload url and the image's Blobkey, so I can store the Blobkey in the DataStore.

I've tried this code, but my image isn't uploading:

Servlet (Return upload url)

BlobstoreService blobstoreService = BlobstoreServiceFactory
            .getBlobstoreService();
public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        UploadOptions uploadOptions = UploadOptions.Builder
                .withGoogleStorageBucketName("photobucket11")
                .maxUploadSizeBytes(1048576);
        String blobUploadUrl = blobstoreService.createUploadUrl("/upload",
                uploadOptions);

        // String blobUploadUrl = blobstoreService.createUploadUrl("/uploaded");

        resp.setStatus(HttpServletResponse.SC_OK);
        resp.setContentType("text/plain");

        PrintWriter out = resp.getWriter();
        out.print(blobUploadUrl);

    }

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        doGet(req, resp);
    }

Code : Android client

Bitmap bmp = BitmapFactory.decodeFile(imagePath);
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                bmp.compress(CompressFormat.JPEG, 75, out);
                byte[] imgByte = out.toByteArray();
                String encodedImage = Base64.encodeToString(imgByte,
                        Base64.DEFAULT);

                HttpClient httpClient = new DefaultHttpClient();                    
                HttpGet httpGet = new HttpGet(
                        "app-url/ImgUpload");
                HttpResponse response = httpClient.execute(httpGet);
                HttpEntity urlEntity = response.getEntity();
                InputStream in = urlEntity.getContent();
                String str = "";
                while (true) {
                    int ch = in.read();
                    if (ch == -1)
                        break;
                    str += (char) ch;
                }

This will return upload url in form of /_ah/upload/akjdhjahdjaudshgaajsdhjsdh which I can use to store the image.

This code uses the url to store the image:

httpClient = new DefaultHttpClient();
                HttpPost postRequest = new HttpPost(str);
                ByteArrayBody bab = new ByteArrayBody(imgByte, "forest.jpg");

                MultipartEntity reqEntity = new MultipartEntity(
                        HttpMultipartMode.BROWSER_COMPATIBLE);

                reqEntity.addPart("uploaded", bab);
                reqEntity.addPart("photoCaption", new StringBody("sfsdfsdf"));
                postRequest.setEntity(reqEntity);
                response = httpClient.execute(postRequest);

                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(
                                response.getEntity().getContent(), "UTF-8"));
                String sResponse;
                StringBuilder s = new StringBuilder();

                while ((sResponse = reader.readLine()) != null) {
                    s = s.append(sResponse);
                }

Here, if I check value of the String s, it shows null. That means it is returning a null response. I don't know what the problem is with this code. Please guide me to solve this problem.

Wisp answered 4/3, 2014 at 10:13 Comment(0)
W
8

After many tries i solved this problem. To store image in blobstore, first android needs to make request to servlet which will generate upload url :

Android client : It will request to generate url and gets url from servlet

HttpClient httpClient = new DefaultHttpClient();    
//This will invoke "ImgUpload servlet           
HttpGet httpGet = new HttpGet("my-app-url/ImgUpload"); 
HttpResponse response = httpClient.execute(httpGet);
HttpEntity urlEntity = response.getEntity();
InputStream in = urlEntity.getContent();
String str = "";
while (true) {
    int ch = in.read();
    if (ch == -1)
        break;
    str += (char) ch;
}

ImgUpload.java - Servlet to generate url and sends response to client

BlobstoreService blobstoreService = BlobstoreServiceFactory
            .getBlobstoreService();
public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

//"uploaded" is another servlet which will send UploadUrl and blobkey to android client
String blobUploadUrl = blobstoreService.createUploadUrl("/uploaded"); 

        resp.setStatus(HttpServletResponse.SC_OK);
        resp.setContentType("text/plain");

        PrintWriter out = resp.getWriter();
        out.print(blobUploadUrl);
    }

In android client,write below code upload image to returned response from above servlet.

//Save image to generated url
HttpPost httppost = new HttpPost(str);
File f = new File(imagePath);
FileBody fileBody = new FileBody(f);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("file", fileBody);
httppost.setEntity(reqEntity);
response = httpClient.execute(httppost); //Here "uploaded" servlet is automatically       invoked
urlEntity = response.getEntity(); //Response will be returned by "uploaded" servlet in JSON format
in = urlEntity.getContent();
str = "";
while (true) {
    int ch = in.read();
    if (ch == -1)
        break;
    str += (char) ch;
}
JSONObject resultJson = new JSONObject(str);
String blobKey = resultJson.getString("blobKey");
String servingUrl = resultJson.getString("servingUrl");

uploaded.java- servlet which returns Uploadurl and Blobkey of image

BlobstoreService blobstoreService = BlobstoreServiceFactory
            .getBlobstoreService();

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        try {
            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);

            resp.setStatus(HttpServletResponse.SC_OK);
            resp.setContentType("application/json");

            JSONObject json = new JSONObject();

            json.put("servingUrl", servingUrl);
            json.put("blobKey", blobKey.getKeyString());

            PrintWriter out = resp.getWriter();
            out.print(json.toString());
            out.flush();
            out.close();
        } catch (JSONException e) {

            e.printStackTrace();
        }

    }
Wisp answered 5/3, 2014 at 5:21 Comment(7)
I wrote quite the same code than you but my path_success is never call. I think it's not call because when I create my file in my android code : File f = new File(uri.getpath()); this file f has a lenght of 0, so I send nothing to upload. Do you know why i have this behavior?Playback
what is path_success ? and are you saving file before you upload it?Wisp
In the uploaded.java-servlet, where do you get the class JSONObject? Where is it imported from?Paginal
@tschakkkiiiii JSONObject is import from com.google.appengine.labs.repackaged.org.json.JSONObjectWisp
@zanky thanx for the answer,it helped allot.Just to be sure 'my-app-url' is something like myAppIdName.appspot.com, right? myAppName being the name of appEngine project at link ?Boater
@FlyingMonkey yes it would be like this.Wisp
Does this still work? @FlyingMonkey Didn't they switch to Google Cloud Storage?Current
B
5

Thanks to zanky I managed to understand it and I want to add my code because some code is deprecated in his answer and also some code need more explanation like overriding and asynctask. By the way the code may not work on local server because of the localhost and IP confusion. Try on the app engine when you are ready.

Servlet-1 BlobUrlGet. This will go to appengine side. This servlet produces upload url for the post method in the client code.

public class BlobUrlGet extends HttpServlet{

    BlobstoreService blServ = BlobstoreServiceFactory.getBlobstoreService();

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        String blobUploadUrl = blServ.createUploadUrl("/blobupload"); 

        resp.setStatus(HttpServletResponse.SC_OK);
        resp.setContentType("text/plain");

        PrintWriter out = resp.getWriter();
        out.print(blobUploadUrl);
    }

}

Servlet-2 BlobUpload This code will be automatically called when the post is done to blobstore. As a result it will give us blobkey and serving url to download the image later.

public class BlobUpload extends HttpServlet {
    BlobstoreService blobstoreService = BlobstoreServiceFactory
            .getBlobstoreService();

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        try {
            List<BlobKey> blobs = blobstoreService.getUploads(req).get("photo");
            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();

            json.put("servingUrl", servingUrl);
            json.put("blobKey", blobKey.getKeyString());

            PrintWriter out = resp.getWriter();
            out.print(json.toString());
            out.flush();
            out.close();
        } catch (JSONException e) {

            e.printStackTrace();
        }

    }
}

Android Client side code. This asynctask will call the servlets and do the post to blobstore with the info it gets.

    private class GetBlobUrlTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... arg0){  

        HttpClient httpClient = new DefaultHttpClient();    
        //This will invoke "ImgUpload servlet           
        HttpGet httpGet = new HttpGet("http://PUT_YOUR_URL_HERE/bloburlget"); 
        HttpResponse response;
        try {
            response = httpClient.execute(httpGet);         
            HttpEntity urlEntity = response.getEntity();
            InputStream in = urlEntity.getContent();
            String str = ""; 
            StringWriter writer = new StringWriter();
            String encoding = "UTF-8";
            IOUtils.copy(in, writer, encoding);
            str = writer.toString();
                HttpPost httppost = new HttpPost(str);
                File f = new File(picturePath);
                MultipartEntityBuilder reqEntity = MultipartEntityBuilder.create();
                reqEntity.addBinaryBody("photo", f, ContentType.create("image/jpeg"), "foto2.jpg");
                httppost.setEntity(reqEntity.build());
                response = httpClient.execute(httppost); //Here "uploaded" servlet is automatically       invoked
                str = EntityUtils.toString(response.getEntity());
                JSONObject resultJson = new JSONObject(str);
                blobKey = resultJson.getString("blobKey");
                servingUrl = resultJson.getString("servingUrl");

        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;

    }
    }

After all we need to update web.xml to be able to execute servlets.

  <servlet>
    <servlet-name>BlobUrlGet</servlet-name>
    <servlet-class>PUT_YOUR_PACKAGE_NAME.BlobUrlGet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>BlobUrlGet</servlet-name>
    <url-pattern>/bloburlget</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>BlobUpload</servlet-name>
    <servlet-class>PUT_YOUR_PACKAGE_NAME.BlobUpload</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>BlobUpload</servlet-name>
    <url-pattern>/blobupload</url-pattern>
  </servlet-mapping>
Bowker answered 3/12, 2014 at 18:49 Comment(1)
Do you know what gradle compile commands would be needed to get the MultipartEntityBuilder and ContentType working in this example? I seem to be having alot of trouble with the apache mime versionSundries
R
2

I am working with endpoints in Android Studio, thanks to SAVANTE, I can finish my code but I had to make small adjustments.

in Servlet 1: I used Endpoints, with this I can handle very easy the OAuth2 in my method:

@ApiMethod(name = "getBlobURL",  scopes = {Constants.EMAIL_SCOPE},
            clientIds = {Constants.WEB_CLIENT_ID,
                    Constants.ANDROID_CLIENT_ID,
                    com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
            audiences = {Constants.ANDROID_AUDIENCE})
    public BlobAttributes getBlobURL(User user) throws  UnauthorizedException, 
     ConflictException{

        //If if is not null, then check if it exists. If yes, throw an Exception
        //that it is already present
        if (user == null){
            throw new UnauthorizedException("User is Not Valid");
        }

        BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
        String blobUploadUrl = blobstoreService.createUploadUrl("/blobupload");

        //BlobAttributes is a class 
        BlobAttributes ba= new BlobAttributes();
        ba.setBlobURL(blobUploadUrl);
        return ba;
    }

My Backend in endpoints Android Studio, do not let me use JSONObject for this rason I make my own Json: in Servlet 2:

String myJson = "{'servingUrl': '" + servingUrl +
                        "', 'blobKey': '" + blobKey.getKeyString() + "'}";

            PrintWriter out = resp.getWriter();
            out.print(myJson);
            out.flush();
            out.close();

I hope works for somebody else, I spent 48 hours trying to understand and operate Blobstore.

Edit:

For make a authenticated call from client this is the way using Google credentials:

accountName = settings.getString(start.KEY_ACCOUNT_NAME, null); //Email account that you before save it
            credential = GoogleAccountCredential.usingAudience(getActivity(),
                    start.WEB_CLIENT_ID); //WEB_CLIENT_ID is your WEB ID in Google Console
            credential.setSelectedAccountName(accountName);

When build your Endpoint put your credential:

PostEndpoint.Builder builder = new PostEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), credential)
                        .setRootUrl(getActivity().getString(R.string.backend_url_connection));
                myApiService = builder.build();

For Get the Account Name of the client, use Plus API

accountName = Plus.AccountApi.getAccountName(mGoogleApiClient);

Read the links down in the comments, with Google documentation for good understand this.

Renteria answered 30/12, 2014 at 23:17 Comment(7)
Hey Carlos, can you edit or add full code of servlet 1. This can help me a lot. Also the method how you call servlet 1 with oauth would be great.Bowker
Hi Savante, Are you intersed in how to use OAuth2 from the beginning? Are you working with Endpoints in Android Studio?Renteria
I was using Eclipse but now I am trying to understand Android Studio since Google suggests this way. I wouldn't say no to any information. :)Bowker
It is much subject to put here and is not related to the question of this post. Then I will give you the tutorial I followed for authentication: cloud.google.com/appengine/docs/java/endpoints/authRenteria
Now for authenticated calls from client: cloud.google.com/appengine/docs/java/endpoints/…Renteria
I add above code that will you need after read the links above.Renteria
Thanks, any chance of getting a skeleton of it. I'm a bit confused on how to replace List<BlobKey> blobs = blobstoreService.getUploads(req).get("photo"); with an end point since we don't have the req variable with an HttpServletRequestSundries

© 2022 - 2024 — McMap. All rights reserved.