为 Google 个云存储对象生成 public link

generate public link for Google Cloud Storage objects

如何为用户通过 BlobStore API 提交的 Google 云存储对象生成 public link?

如何为 Google 云存储对象生成 public link?

  1. 通过 console 手动创建服务帐户并生成 .p12 密钥文件。
  2. 用户将文件上传到 URL 后,使用以下方法生成:
blobstoreService.createUploadUrl("/fileUploadingHandler", 
        UploadOptions.Builder.withMaxUploadSizeBytes(1024*1024*10) // 10 MB max
                             .googleStorageBucketName(BUCKET_NAME));

servlet(处理 /fileUploadingHandler)可以检索 GCS 对象的文件名并生成临时签名 public link,如下所示:

Map<String, List<FileInfo>> fileInfoMap = blobstoreService.getFileInfos(request);
List<FileInfo> fileInfos = fileInfoMap.get("fileName");
FileInfo fileInfo = fileInfos.get(0);
String[] parts = fileInfo.getGsObjectName().split("/"); // get rid of /gs/buck_name/
String fileName = parts[parts.length - 1];
String signedUrl = GcsUrlSigner.generateSignedUrl(fileName);
// send the temporary public link (signedUrl) back to the user
import com.google.api.client.util.Base64;

import java.io.InputStream;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;

/**
 * Created by Fouad on 22-Dec-15.
 */
public class GcsUrlSigner
{
    private static final String DEFAULT_BUCKET_NAME = "XXXXXXXX.appspot.com";
    private static final String PUBLIC_URL_SERVICE_ACCOUNT_EMAIL = "XXXXXXXX@XXXXXX.iam.gserviceaccount.com";
    private static final String PUBLIC_URL_SERVICE_ACCOUNT_PKCS12_FILE_PATH = "XXXXXX.p12"; // located in the same folder as GcsUrlSigner.java
    private static final String PUBLIC_URL_SERVICE_ACCOUNT_PKCS12_FILE_PASSWORD = "notasecret";
    private static final long PUBLIC_URL_EXPIRATION_SECONDS = System.currentTimeMillis() / 1000 + 60; // 60 seconds

    public static String generateSignedUrl(String objectName) throws Exception
    {
        return generateSignedUrl(DEFAULT_BUCKET_NAME, objectName);
    }

    public static String generateSignedUrl(String bucketName, String objectName) throws Exception
    {
        PrivateKey key = loadKeyFromPkcs12(PUBLIC_URL_SERVICE_ACCOUNT_PKCS12_FILE_PATH, PUBLIC_URL_SERVICE_ACCOUNT_PKCS12_FILE_PASSWORD.toCharArray());
        return getSigningURL(key, "GET", PUBLIC_URL_EXPIRATION_SECONDS, bucketName, objectName);
    }

    private static String getSigningURL(PrivateKey key, String verb, long expirationSeconds, String bucketName, String objectName) throws Exception
    {
        String url_signature = signString(key, verb + "\n\n\n" + expirationSeconds + "\n" + "/" + bucketName + "/" + objectName);
        String signed_url = "https://storage.googleapis.com/" + bucketName + "/" + objectName +
                "?GoogleAccessId=" + PUBLIC_URL_SERVICE_ACCOUNT_EMAIL +
                "&Expires=" + expirationMillis +
                "&Signature=" + URLEncoder.encode(url_signature, "UTF-8");
        return signed_url;
    }

    private static PrivateKey loadKeyFromPkcs12(String filename, char[] password) throws Exception
    {
        InputStream is = GcsUrlSigner.class.getResourceAsStream(PUBLIC_URL_SERVICE_ACCOUNT_PKCS12_FILE_PATH);
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(is, password);
        return (PrivateKey) ks.getKey("privatekey", password);
    }

    private static String signString(PrivateKey key, String stringToSign) throws Exception
    {
        if(key == null) throw new Exception("Private Key not initalized");

        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initSign(key);
        signer.update(stringToSign.getBytes("UTF-8"));

        byte[] rawSignature = signer.sign();

        return new String(Base64.encodeBase64(rawSignature), "UTF-8");
    }
}

有几种方法可以为 public GCS 对象提供服务。一种是签名 URL。另一种是 getServingUrl() App Engine 方法。

但是,如果对象应该对所有人可见,您可以做一些更简单的事情。将对象的权限设置为 "AllUsers" 授予 READ 权限,这允许无需任何身份验证即可读取对象,然后只需将用户引用到此路径:

https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME

就是这样!

您可以通过 API 或使用此 gsutil 命令将对象设置为 public只能从云控制台读取:gsutil acl ch -g AllUsers:R gs://BUCKET_NAME/OBJECT_NAME