【问题标题】:Store image to Blobstore from android client and retrieve blobkey and upload url to store in Datastore. - GAE从 android 客户端将图像存储到 Blobstore 并检索 blobkey 并上传 url 以存储在 Datastore 中。 - 盖伊
【发布时间】:2014-04-05 19:25:51
【问题描述】:

在我的 Android 应用程序中,我想将图像上传到 Blobstore,然后检索上传 url 和图像的 Blobkey,这样我就可以将 Blobkey 存储在 DataStore 中。

我试过这段代码,但我的图片没有上传:

Servlet(返回上传网址)

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);
    }

代码:安卓客户端

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;
                }

这将以/_ah/upload/akjdhjahdjaudshgaajsdhjsdh 的形式返回上传网址,我可以使用它来存储图像。

这段代码使用url来存储图片:

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);
                }

这里,如果我检查字符串s 的值,它会显示null。这意味着它返回一个空响应。我不知道这段代码有什么问题。请指导我解决这个问题。

【问题讨论】:

    标签: android google-app-engine google-cloud-datastore blobstore


    【解决方案1】:

    经过多次尝试,我解决了这个问题。要将图像存储在 blobstore 中,首先 android 需要向 servlet 发出请求,servlet 将生成上传 url:

    Android客户端:请求生成url并从servlet获取url

    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 生成 url 并向客户端发送响应

    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);
        }
    

    在android客户端中,编写下面的代码上传图片到上面的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 返回图片的 Uploadurl 和 Blobkey

    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();
            }
    
        }
    

    【讨论】:

    • 我写的代码和你完全一样,但我的 path_success 从来没有被调用过。我认为这不是调用,因为当我在我的 android 代码中创建文件时: File f = new File(uri.getpath());这个文件 f 的长度为 0,所以我什么都不发送。你知道我为什么会有这种行为吗?
    • path_success 是什么?你在上传之前保存文件吗?
    • 在uploaded.java-servlet 中,你从哪里得到JSONObject 类?从哪里进口的?
    • @tschakkkiiiii JSONObject 是从 com.google.appengine.labs.repackaged.org.json.JSONObject 导入的
    • @zankythanx 的答案,它帮助分配。只是为了确保“my-app-url”类似于 myAppIdName.appspot.com,对吗? myAppName 是 link 的 appEngine 项目的名称?
    【解决方案2】:

    感谢 zanky,我设法理解了它,我想添加我的代码,因为在他的答案中不推荐使用某些代码,并且某些代码需要更多解释,例如覆盖和异步任务。顺便说一句,由于本地主机和 IP 混淆,代码可能无法在本地服务器上运行。准备好后尝试应用引擎。

    Servlet-1 BlobUrlGet。这将转到 appengine 方面。此 servlet 为客户端代码中的 post 方法生成上传 url。

    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 当发布到 blobstore 时,将自动调用此代码。结果,它将为我们提供 blobkey 和服务 url,以便稍后下载图像。

    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 客户端代码。这个 asynctask 将调用 servlet 并使用它获得的信息发布到 blobstore。

        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;
    
        }
        }
    

    毕竟我们需要更新 web.xml 才能执行 servlet。

      <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>
    

    【讨论】:

    • 你知道在这个例子中让 MultipartEntityBuilder 和 ContentType 工作需要什么 gradle 编译命令吗?我似乎在使用 apache mime 版本时遇到了很多麻烦
    【解决方案3】:

    我正在使用 Android Studio 中的端点,感谢 SAVANTE,我可以完成我的代码,但我必须进行一些小的调整。

    在 Servlet 1 中: 我使用了 Endpoints,这样我可以在我的方法中非常轻松地处理 OAuth2:

    @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;
        }
    

    我在端点 Android Studio 中的后端,不要让我为此使用 JSONObject 我自己制作 Json: 在 Servlet 2 中:

    String myJson = "{'servingUrl': '" + servingUrl +
                            "', 'blobKey': '" + blobKey.getKeyString() + "'}";
    
                PrintWriter out = resp.getWriter();
                out.print(myJson);
                out.flush();
                out.close();
    

    我希望对其他人有用,我花了 48 小时试图了解和操作 Blobstore。

    编辑:

    对于来自客户端的经过身份验证的呼叫,这是使用 Google 凭据的方式:

    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);
    

    在构建您的端点时放置您的凭据:

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

    要获取客户端的帐户名称,请使用 Plus API

    accountName = Plus.AccountApi.getAccountName(mGoogleApiClient);
    

    阅读 cmets 中的链接,并使用 Google 文档更好地理解这一点。

    【讨论】:

    • 嘿 Carlos,你能编辑或添加 servlet 1 的完整代码吗?这对我有很大帮助。使用 oauth 调用 servlet 1 的方法也很棒。
    • 嗨 Savante,您是否从一开始就对如何使用 OAuth2 感兴趣?您是否在 Android Studio 中使用 Endpoints?
    • 我使用的是 Eclipse,但现在我正在尝试了解 Android Studio,因为 Google 建议采用这种方式。我不会拒绝任何信息。 :)
    • 这里有很多主题,与本文的问题无关。那我就给你我照做的认证教程:cloud.google.com/appengine/docs/java/endpoints/auth
    • 现在来自客户端的经过身份验证的调用:cloud.google.com/appengine/docs/java/endpoints/…
    猜你喜欢
    • 2019-10-18
    • 2016-08-09
    • 2014-11-25
    • 2021-03-20
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    • 1970-01-01
    相关资源
    最近更新 更多