使用blobstore与谷歌云端点和android

Pou*_*ald 32 google-app-engine android blobstore gae-eclipse-plugin google-cloud-endpoints

我正在使用eclipse插件开发一个app-engine连接的android项目.该应用程序的一个方面是允许用户Alpha将图片发送给用户Bravo.为此,我有以下设置:

用户Alpha发布:

  • 通过端点将图像发送到我的应用引擎服务器
  • 服务器将图像存储在blob存储中
  • 服务器在数据存储区中存储blobkey

用户Bravo获得:

  • 服务器从数据存储区获取blobkey
  • 服务器使用blob密钥获取图像
  • 服务器使用端点将图像发送到Android应用程序

这个设置需要两(2)分钟,从我的Android应用程序发送图像到我可以在blob疼痛中看到它.不用说这是完全不可接受的.

我的服务器通过以下代码以编程方式处理图像:

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);
    }
Run Code Online (Sandbox Code Playgroud)

有谁知道如何调整官方示例以使用端点(在我必须使用我的app-engine实例的情况下)或使用getServingUrl(绕过我的实例)来存储和提供我的blob?
请包含代码,而不是单词.谢谢.

Joa*_*him 32

我将分享我是如何做到这一点的.我没有使用google-cloud-endpoints,只是我自己的基于api的api,但无论哪种方式都应该是相同的想法.

我会用代码一步一步地解决它,希望它会很清楚.您只需调整发送请求的方式来使用端点,而不是像本例中那样更通用.我包括一些样板,但为了简洁起见,不包括try/catch,错误检查等.

第1步(客户)

第一个客户端从服务器请求上传URL:

HttpClient httpclient = new DefaultHttpClient();    
HttpConnectionParams.setConnectionTimeout(httpclient.getParams(), 10000); //Timeout Limit

HttpGet httpGet = new HttpGet("http://example.com/blob/getuploadurl");
response = httpclient.execute(httpGet);
Run Code Online (Sandbox Code Playgroud)

第2步(服务器)

在服务器端,上传请求servlet看起来像这样:

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();
Run Code Online (Sandbox Code Playgroud)

请注意createUploadUrl的参数.这是实际上传完成后客户端将被重定向的位置.这就是您将处理存储blobkey和/或服务URL并将其返回给客户端的位置.您必须将servlet映射到该URL,该URL将处理第4步

步骤3(客户端) 再次返回客户端,使用从步骤2返回的URL将实际文件发送到上传URL.

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)
Run Code Online (Sandbox Code Playgroud)

在步骤2中将此请求发送到servlet后,它将被重定向到您在createUploadUrl()前面指定的servlet

第4步(服务器)

回到服务器端:这是处理映射到的url的servlet blob/upload.我们将在这里返回blobkey并在json对象中将url提供给客户端:

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();
Run Code Online (Sandbox Code Playgroud)

第5步(客户端)

我们将从json获取blobkey并提供url,然后将其与用户ID等一起发送到数据存储区实体中.

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
Run Code Online (Sandbox Code Playgroud)

步骤6(服务器) 实际上将所有内容存储在数据存储区中(在此示例中使用objectify)

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);
Run Code Online (Sandbox Code Playgroud)

我希望这会让事情更清楚.如果有人想编辑使用云端点的答案而不是这个更通用的例子,请随意:)

关于服务网址

服务网址是向客户提供图像的绝佳方式,因为它可以动态地动态缩放图像.例如,您可以通过简单地附加=sXXX在服务URL的末尾来向LDPI用户发送较小的图像.其中XXX是图像最大尺寸的像素大小.您可以完全避免使用实例,只需为带宽付费,用户只需下载所需内容.

PS!

应该可以在步骤4停止并直接将其存储在那里,通过在步骤3中传递userId f.ex.任何参数都应该发送到步骤4,但我没有得到它,所以这我是如何做到这一点的,所以我这样分享,因为我知道它有效.

  • 你可以使用端点而不是第一个servlet @ApiMethod(name ="getBlobURL")public BlobAttributes getBlobURL()throws Exception {BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService(); String blobUploadUrl = blobstoreService.createUploadUrl("/ getURL"); BlobAttributes ba = new BlobAttributes(); ba.setBlobURl(blobUploadUrl); 返回ba; } (3认同)

Flo*_*Flo 5

我使用这个问题的答案来构建我自己的使用AppEngine端点的系统.与上面的帖子不同,我希望有一个干净的API直接将图像(作为字节数组)传输到Google Endpoint,并且上传到BlobstorageService是在后端完成的.这样做的好处是我有一个原子API.显然缺点是服务器上的负载以及客户端上的大量编组操作.

Android - 加载,缩放和序列化图像并上传到端点

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();
}
Run Code Online (Sandbox Code Playgroud)

后端API端点 - 将图像上传到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 ...
}
Run Code Online (Sandbox Code Playgroud)

处理来自BlobstorageService的回调的Servlet

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

剩下要做的唯一事情是绑定/blob/upload到UploadBlobServlet.

注意:当AppEngine在本地运行时,这似乎不起作用(如果在本地执行,那么POST到BlobstorageService将始终返回404 NOT FOUND)