有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在Google云存储中使用getServingUrl()方法时抛出错误

我以以下格式将对象存储在Google云存储中

bucketname/UUID/object

我有一个java方法,返回以下格式的下载链接以查看文件

https://storage.googleapis.com/bucket/uuid/object?GoogleAccessId

现在,我要做的是编写一个java方法,在图像的情况下调整对象的大小

编辑:这是连接到地面军事系统的类

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.Base64;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.api.gax.paging.Page;
import com.google.api.services.storage.StorageScopes;
import com.google.api.services.storage.model.Notification;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.ServingUrlOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.cloud.pubsub.v1.SubscriptionAdminSettings;
import com.google.cloud.pubsub.v1.TopicAdminClient;
import com.google.cloud.pubsub.v1.TopicAdminSettings;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.HttpMethod;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.Storage.BlobListOption;
import com.google.cloud.storage.Storage.SignUrlOption;
import com.google.cloud.storage.StorageClass;
import com.google.cloud.storage.StorageOptions;
import com.google.pubsub.v1.ProjectSubscriptionName;
import com.google.pubsub.v1.ProjectTopicName;
import com.google.pubsub.v1.PushConfig;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * This class connects to the Google cloud storage and handles bucket/object related operations.
 *
 * @author Roshan
 */
public class GoogleCloudStorage {

  private ServiceAccountCredentials creds;
  private String saEmail;
  private Storage storage;
  private ClassLoader classloader;
  private TopicAdminSettings topicAdminSettings;
  private SubscriptionAdminSettings subscriptionAdminSettings;
  private String projectId;
  private Logger log;

  public GoogleCloudStorage() throws IOException {
    log = LoggerFactory.getLogger(this.getClass());
    /* Initialize credentials, service account email and other required stuffs. */
    this.classloader = Thread.currentThread().getContextClassLoader();
    InputStream is = classloader.getResourceAsStream("Key.json");
    if (is != null) {
      this.creds = ServiceAccountCredentials.fromStream(is);
    } else {
      log.error("Inputstream from credentials file cannot be null.");
    }
    this.storage = StorageOptions.newBuilder()
        .setCredentials(creds)
        .build()
        .getService();
    this.saEmail = creds.getClientEmail();
    this.projectId = creds.getProjectId();
    this.topicAdminSettings = TopicAdminSettings.newBuilder()
        .setCredentialsProvider(FixedCredentialsProvider.create(creds)).build();
    this.subscriptionAdminSettings = SubscriptionAdminSettings.newBuilder()
        .setCredentialsProvider(FixedCredentialsProvider.create(creds)).build();
  }

  /**
   * @param bucketName String
   *
   * This method creates a bucket, named as the given argument.
   */
  public void createBucket(String bucketName) {
    String topicId = "give topic id here";
    if (checkIfBucketExists(bucketName)) {
      log.info("Bucket already exists!");
    }
    storage.create(
        BucketInfo.newBuilder(bucketName)
            // See here for possible values: http://g.co/cloud/storage/docs/storage-classes
            .setStorageClass(StorageClass.COLDLINE)
            // Possible values: http://g.co/cloud/storage/docs/bucket-locations#location-mr
            .setLocation("europe-north1")
            .build());
    setBucketNotification(bucketName, topicId);
  }

  /**
   * @param bucketName String
   * @param uuid String
   * @param objectName String
   * @param mimeType String
   * @return String
   * @throws IOException This method sends POST request to the signed URL using custom headers and
   * an empty body, which returns the actual upload location in the response header.
   */
  public String getUploadLink(String bucketName, String uuid, String objectName, String mimeType)
      throws IOException {
    if (!checkIfBucketExists(bucketName)) {
      createBucket(bucketName);
    }
    URL myURL = new URL(getSignedUrlToPost(bucketName, uuid, objectName, mimeType));
    HttpURLConnection myURLConnection = (HttpURLConnection) myURL.openConnection();
    myURLConnection.setRequestMethod("POST");
    myURLConnection.setRequestProperty("Content-Type", mimeType);
    myURLConnection.setRequestProperty("x-goog-resumable", "start");
    // Send post request
    myURLConnection.setDoOutput(true);
    DataOutputStream wr = new DataOutputStream(myURLConnection.getOutputStream());
    wr.flush();
    wr.close();
    int responseCode = myURLConnection.getResponseCode();
    if (responseCode != 201) {
      log.error("Request Failed");
    }
    return myURLConnection.getHeaderField("Location");
  }

  /**
   * @param bucketName String
   * @param blobName String
   * @return String
   *
   * This method returns a downloadlink, valid for 10 minutes, based on the given bucket and object
   * name.
   */
  public String getDownloadLink(String bucketName, String blobName) {
    return storage.signUrl(
        BlobInfo.newBuilder(bucketName, blobName).build(),
        10,
        TimeUnit.MINUTES,
        SignUrlOption.httpMethod(HttpMethod.GET)
    ).toString();
  }

  /**
   * @param bucketName String
   * @param uuid String
   * @return String
   *
   * This method returns an object name, based on the given bucketname and uuid.
   */
  public String getFileName(String bucketName, String uuid) {
    final String[] fileName = {null};
    Page<Blob> blobs = storage.list(bucketName, BlobListOption.prefix(uuid));
    blobs.iterateAll().forEach(
        blob -> fileName[0] = blob.getName()//.substring(blob.getName().lastIndexOf('/') + 1)
    );
    return fileName[0];
  }

  /**
   * @param bucketName String
   * @param uuid String
   * @param objectName String
   * @param mimeType String
   * @return String
   *
   * This method signs and return the URL to POST, using credentials from above. An uploadlink is
   * generated after a POST request is sent to the URL returned by this method, as used by
   * 'getDownloadLink()' above.
   */
  private String getSignedUrlToPost(String bucketName, String uuid, String objectName,
      String mimeType) {
    String signedUrl = null;
    try {
      String verb = "POST";
      long expiration = System.currentTimeMillis() / 1000 + 60;
      String canonicalizedExtensionHeaders = "x-goog-resumable:start";

      byte[] sr = creds.sign(
          (verb + "\n\n" + mimeType + "\n" + expiration + "\n" + canonicalizedExtensionHeaders
              +
              "\n" + "/" + bucketName + "/" + uuid + "/" + objectName).getBytes());
      String urlSignature = new String(Base64.encodeBase64(sr));
      signedUrl = "https://storage.googleapis.com/" + bucketName + "/" + uuid + "/" + objectName +
          "?GoogleAccessId=" + saEmail +
          "&Expires=" + expiration +
          "&Signature=" + URLEncoder.encode(urlSignature, "UTF-8");
    } catch (Exception ex) {
      log.error("Caught an Exception {}", ex);
    }
    return signedUrl;
  }

  /**
   * @param topicName String
   * @throws IOException This method creates a topic, based on the given topic name, needed to
   * publish/subscribe messages.
   */
  public void createTopic(String topicName) throws IOException {
    try (TopicAdminClient topicAdminClient = TopicAdminClient.create(topicAdminSettings)) {
      ProjectTopicName projectTopicName = ProjectTopicName.of(projectId, topicName);
      topicAdminClient.createTopic(projectTopicName);
    }
  }

  /**
   * @param topicId String
   * @param subscriptionName String
   * @throws IOException This method creates a subscription, that handles pull message delivery.
   */
  public void createSubscription(String topicId, String subscriptionName)
      throws IOException {
    try (SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient
        .create(subscriptionAdminSettings)) {
      ProjectTopicName topic = ProjectTopicName.of(projectId, topicId);
      ProjectSubscriptionName subscription = ProjectSubscriptionName
          .of(projectId, subscriptionName);
      subscriptionAdminClient
          .createSubscription(subscription, topic, PushConfig.getDefaultInstance(), 10);
    }
  }

  public String getResizedImage(String bucketName, String uuid) {
    GcsFilename gcsFilename = new GcsFilename(bucketName, getFileName(bucketName, uuid));
    ImagesService is = ImagesServiceFactory.getImagesService();
    String filename = String.format("/gs/%s/%s", gcsFilename.getBucketName(), gcsFilename.getObjectName());
    return is.getServingUrl(ServingUrlOptions.Builder.withGoogleStorageFileName(filename));
  }

  /**
   * @param bucketName String
   * @param topicId String
   *
   * This method sets bucket notification that publish messages on the event of an upload.
   */
  private void setBucketNotification(String bucketName, String topicId) {
    List<String> eventType = new ArrayList<>();
    eventType.add("OBJECT_FINALIZE");
    try {
      Notification notification = new Notification();
      notification.setTopic(topicId);
      notification.setEventTypes(eventType);
      notification.setPayloadFormat("JSON_API_V1");

      final GoogleCredential googleCredential = GoogleCredential
          .fromStream(Objects.requireNonNull(classloader.getResourceAsStream("Key.json")))
          .createScoped(Collections.singletonList(StorageScopes.DEVSTORAGE_FULL_CONTROL));

      final com.google.api.services.storage.Storage myStorage = new com.google.api.services.storage.Storage.Builder(
          new NetHttpTransport(), new JacksonFactory(), googleCredential).build();

      Notification v = myStorage.notifications().insert(bucketName, notification).execute();
      log.info("{}", v);
    } catch (IOException e) {
      log.error("Caught an IOException {}", e);
    }
  }

  /**
   * @param bucketName String
   * @return boolean
   *
   * This method checks whether a bucket with given bucket name exists or not and returns a boolean
   * value of either true or false.
   */
  public boolean checkIfBucketExists(String bucketName) {
    return storage.get(bucketName, Storage.BucketGetOption.fields()) != null;
  }
}

这是Junit测试类

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import service.GoogleCloudStorage;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class GoogleCloudOperationsTest {

  private GoogleCloudStorage service;
  private Logger log;      

  @Before
  public void setup() throws IOException {
    service = new GoogleCloudStorage();
    log = LoggerFactory.getLogger(this.getClass());        
  }

  @Test
  public void testIfBucketExist() {
    assertTrue(service.checkIfBucketExists("bucketName"));
  }   


  @Test
  public void testDownloadLinkGeneration() {
    String uuid = "uuid";
    String objectName = service.getFileName("bucket", uuid);
    String downloadLink = service.getDownloadLink("uuid", objectName);
    assertNotNull(downloadLink);
    if (!StringUtils.isEmpty(downloadLink)) {
      log.info(downloadLink);
    }
  }

  @Test
  public void testDownloadLinkGenerationAfterResize() {
    String uuid = "uuid";
    String objectName = service.getFileName("bucket", uuid);
    String downloadLink = service.getResizedImage("bucket", objectName);
    assertNotNull(downloadLink);
    log.info(downloadLink);
  }    
}

这是pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>google-cloud-operations</groupId>
  <artifactId>google-cloud-operations</artifactId>
  <version>1.0-SNAPSHOT</version>

  <dependencies>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.5</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.5</version>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-pubsub</artifactId>
      <version>1.72.0</version>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-storage</artifactId>
      <version>1.72.0</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.google.appengine.tools</groupId>
      <artifactId>appengine-gcs-client</artifactId>
      <version>0.8</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <inherited>true</inherited>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

凭证文件Key.json位于resources目录内。 但是,当我测试getResizedImage(String bucketName, String uuid, Integer size)时,它抛出以下错误

com.google.apphosting.api.ApiProxy$CallNotFoundException: Can't make API call blobstore.CreateEncodedGoogleStorageKey in a thread that is neither the original request thread nor a thread created by ThreadManager

at com.google.apphosting.api.ApiProxy$CallNotFoundException.foreignThread(ApiProxy.java:800)
at com.google.apphosting.api.ApiProxy.makeSyncCall(ApiProxy.java:112)
at com.google.apphosting.api.ApiProxy.makeSyncCall(ApiProxy.java:65)
at com.google.appengine.api.blobstore.BlobstoreServiceImpl.createGsBlobKey(BlobstoreServiceImpl.java:312)
at com.google.appengine.api.images.ImagesServiceImpl.getServingUrl(ImagesServiceImpl.java:266)
at service.GoogleCloudStorage.getResizedImage(GoogleCloudStorage.java:240)
at operationstest.GoogleCloudOperationsTest.testDownloadLinkGenerationAfterResize(GoogleCloudOperationsTest.java:64)

错误正在发生

return is.getServingUrl(ServingUrlOptions.Builder.withGoogleStorageFileName(filename).imageSize(size).crop(true));

我不知道是什么原因造成的。我真的需要一些帮助

编辑:我的解决方案尝试基于此answer


共 (0) 个答案