我正在使用eclipse插件开发一个app-engine连接的android项目。该应用程序的一个方面是允许用户Alpha将图片发送给用户Bravo。为此,我有以下设置:
用户Alpha发布:
用户Bravo获取:
这个设置需要两(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);
}
有谁知道如何调整the official example以使用端点(在我必须使用我的app-engine实例的情况下)或使用getServingUrl
(绕过我的实例)来存储和提供我的端点斑点?
请包含代码,而不是单词。感谢。
答案 0 :(得分: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);
第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();
注意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)
在步骤2中将此请求发送到servlet后,它将被重定向到您之前createUploadUrl()
中指定的servlet
第4步(服务器)
回到服务器端:
这是处理映射到blob/upload
的url的servlet。我们将在这里将blobkey和服务url返回给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();
第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
第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);
我希望这会让事情更清楚。如果有人想编辑使用云端点的答案而不是这个更通用的示例,请随意:)
关于服务网址
服务网址是向客户提供图像的绝佳方式,因为它可以动态地动态缩放图像。例如,您只需在服务网址末尾附加=sXXX
即可向您的LDPI用户发送较小的图片。其中XXX是图像最大尺寸的像素大小。您完全避免使用实例,只需为带宽付费,用户只需下载所需内容。
<强> PS!强>
应该可以在步骤4停止并直接将其存储在那里,通过在步骤3中传递userId f.ex。任何参数都应该发送到第4步,但我没有让它工作,所以这就是我现在这样做的方式,所以我这样分享,因为我知道它有效。
答案 1 :(得分: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();
}
后端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 ...
}
处理来自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();
}
}
唯一要做的就是将/blob/upload
绑定到UploadBlobServlet。
注意:当AppEngine在本地运行时,这似乎不起作用(如果在本地执行,那么POST到BlobstorageService将始终返回404 NOT FOUND)
答案 2 :(得分:2)
由于我尝试了很多方法在端点的api中进行回调服务,所以我放弃了这个方法。但是,我可以解决这个问题,为api端点创建并行servlet,它只需要定义类服务器并添加web.xml配置。在这里我的解决方案:
1点Envice Service获取上传网址 然后使用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。现在客户端可以调用端点获取上传URL:
也许有些像这样(对于android也使用你的客户端库enpoint):
gapi.client.debugendpoint.getUploadURL().execute();
第3。接下来的步骤是在最后一步中收集到的url帖子: 你可以用一个android的httpClient来做,再次,在我的情况下,我需要从网上传然后我使用一个表单,并使用onChangeFile()事件回调获取uploadurl(使用步骤3)然后当它响应更改表单参数“action”和“codeId”在有人决定点击提交按钮之前:
<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最后是paralele servlet类:
@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();
}
}
并添加到web.xml,其中com.apppack是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>