使用GoogleCloudEndpoints访问GoogleCloudStorage

时间:2016-12-20 16:30:29

标签: java android google-app-engine google-cloud-storage google-cloud-endpoints

我正在开展这个项目,我使用通过Google-Cloud-Endpoints连接到Android应用的Google-App-Engine后端。对于Google-Cloud-Datastore访问,我使用Objectify并且一切正常。

现在我决定添加将图片上传到Google-Cloud-Storage的功能,但我无法使用Google-Cloud-Endpoints设置找到有关如何执行此操作的明确说明。

我找到了如何将Google-Cloud-Storage与Google-App-Engine结合使用的以下说明: https://cloud.google.com/appengine/docs/java/googlecloudstorageclient/app-engine-cloud-storage-sample

但不是将其添加到Endpoints Api,而是写一篇额外的servlet。

此外,我发现了Android的上传/下载示例:

github.com / thorrism / GoogleCloudExample

可悲的是,这是使用Google云端存储API直接访问Google-Cloud-Storage,您需要将P12文件添加到资产文件夹,这似乎不安全。

我的Google-App-Engine代码如下:

@Api(
    name = "example",
    version = "v1", 
    scopes = { Constants.EMAIL_SCOPE }, 
    clientIds = { Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID, Constants.API_EXPLORER_CLIENT_ID }, 
    audiences = {Constants.ANDROID_AUDIENCE},
    description = "API for the Example Backend application."

)

public class ExampleApi{

@ApiMethod(name = "doSomething", path = "dosomething", httpMethod = HttpMethod.POST)
public String doSomething(@Named("text") String text){

  TestEntity test = new TestEntity(text);

   ofy().save().entity(test).now();

   return  test;

}

上传后,我生成了Endpoints Client Library并将其导入到我的android项目中。

然后我就像在这里解释的那样从Android调用端点:

https://cloud.google.com/appengine/docs/java/endpoints/calling-from-android#creating_the_service_object

public static com.appspot.******.example.Example buildServiceHandler(Context context, String email) {
    GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(
            context, AppConstants.AUDIENCE);
    credential.setSelectedAccountName(email);

     com.appspot.******.example.Example.Builder builder  = new  com.appspot.******.example.Example.Builder(
            AppConstants.HTTP_TRANSPORT,
            AppConstants.JSON_FACTORY, null);
    builder.setApplicationName("example-server");
    return builder.build();
}

sApiServiceHandler = buildServiceHandlerWithAuth(context,email);

每个Api-Method我都这样称呼:

com.appspot.******.example.Example.DoSomething doSomething = sApiServiceHandler.doSomething(someString);
doSomething.execute();

所有这些都可以正常工作,但仅适用于存储/接收数据存储区实体。如何使用Google Cloud Endpoints设置上传/下载文件到Google云端存储? 是否有可能使用已构建的ServiceHandler将带有我的图像数据的POST通过端点发送到UploadServlet? 是否可以从端点方法调用servlet?我该如何将Post发送到Servlet以及如何进行身份验证?

非常感谢任何帮助或建议!

1 个答案:

答案 0 :(得分:0)

有不同的方法可以执行此操作,但最常用的方法是使用Signed URLs,以便您的Android应用可以直接将文件安全地上传到Google云端存储,而无需通过您的终端后端。基本过程是:

1)创建creates a new signed URL的端点方法并将其返回给Android客户端。在服务器上签名URL仍然需要P12密钥,但存储在App Engine上,而不是客户端,因此是安全的。尝试使用URL的短暂过期,例如不超过5分钟。

2)拥有Android客户端upload the file directly to the signed URL,因为您对HTTP PUT正常Cloud Storage XML API上传文件(resumable uploads with the JSON API)也受支持,但未涵盖这里)。

您的Endpoints方法可能如下所示:

@ApiMethod(name = "getUploadUrl", path = "getuploadurl", httpMethod = HttpMethod.GET)
public MyApiResponse getUploadUrl(@Named("fileName") String fileName
                                  @Named("contentType" String contentType)
{
        String stringToSign
            = "PUT\n" + contentType
            + "\n" + EXPIRATION_TIMESTAMP_EPOCH_SECONDS + "\n"
            + YOUR_GCS_BUCKET + "/" + fileName;

        // Load P12 key
        FileInputStream fileInputStream = new FileInputStream(PATH_TO_P12_KEY);
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(fileInputStream, password);
        PrivateKey key = keyStore.getKey(privatekey", YOUR_P12_KEY_PASSWORD);

        // Get signature
        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initSign(key);
        signer.update(stringToSign.getBytes("UTF-8"));
        byte[] rawSignature = signer.sign();
        String signature = new String(Base64.encodeBase64(rawSignature, false), "UTF-8");

        // Construct signed url
        String url
            = "http://storage.googleapis.com/" + YOUR_GCS_BUCKET + fileName
            + "?GoogleAccessId=" + P12_KEY_SERVICE_ACCOUNT_CLIENT_ID
            + "&Expires=" + EXPIRATION_TIMESTAMP_EPOCH_SECONDS
            + "&Signature=" + URLEncoder.encode(signature, "UTF-8");

        // Endpoints doesn't let you return 'String' directly
        MyApiResponse response = new MyApiResponse();
        response.setString(url);
        return response;
}

在Android方面,您可以使用以下方法:

// Get the upload URL from the API
getUploadUrl = sApiServiceHandler.getUploadUrl(fileName, contentType);
MyApiResponse response = getUploadUrl.execute();
String uploadUrl = response.getString();

// Open connection to GCS
URL url = new URL(uploadUrl);
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setDoOutput(true);
httpConnection.setRequestMethod("PUT");
httpConnection.setRequestProperty("Content-Type", contentType);

// Write file data
OutputStreamWriter out = new OutputStreamWriter(httpConnection.getOutputStream());
out.write(fileData);
out.flush();

// Get response, check status code etc.
InputStreamReader in = new InputStreamReader(httpConnection.getInputStream());
// ...

(免责声明:我只是在文本编辑器中自由输入代码,但实际上没有对其进行测试,但它应该足以让您了解一般。)