Como usar APIs do App Engine para Cloud Storage

Neste documento, descrevemos como armazenar e recuperar dados usando a biblioteca de cliente do App Engine para Cloud Storage. É necessário que você saiba criar um aplicativo do App Engine, conforme descrito em Guia de início rápido do ambiente padrão do App Engine para Java 8. Também pressupomos que você entende os conceitos básicos do uso do Cloud Storage no App Engine.

Como fazer o download da biblioteca de cliente do App Engine para Cloud Storage

Faça o download da biblioteca usando ferramentas conhecidas, como Apache Maven, Apache Ivy ou Git (todos em inglês). Como alternativa, faça isso manualmente no repositório do Maven. Escolha o método preferido:

Git

Se tiver o Git instalado, você poderá clonar o repositório do GitHub da biblioteca de cliente do Google Cloud Storage da seguinte maneira:

git clone https://github.com/GoogleCloudPlatform/appengine-gcs-client.git

Maven

Os usuários do Maven precisam incluir o seguinte no arquivo pom.xml do aplicativo:

<dependency>
    <groupId>com.google.appengine.tools</groupId>
    <artifactId>appengine-gcs-client</artifactId>
    <version>0.7</version>
</dependency>

Ivy

Os usuários do Ivy precisam incluir o seguinte no arquivo ivy.xml do aplicativo:

<dependency org="com.google.appengine.tools"
            name="appengine-gcs-client"
            rev="latest.integration" />

Download manual

Visite o repositório do Maven da biblioteca e faça o download da classe, da fonte e dos arquivos JAR do JavaDoc mais recentes:

Além disso, você precisará fazer o download das seguintes dependências e incluí-las no aplicativo:

Access Control Lists e a biblioteca de cliente do App Engine

Um app que usa a biblioteca de cliente não pode alterar a Access Control List do intervalo, mas pode especificar uma que controle o acesso aos objetos criados por ele. As configurações de Access Control List disponíveis são descritas na documentação da classe FcsFileOptions.

Cloud Storage e subdiretórios

A biblioteca de cliente do App Engine para Cloud Storage permite fornecer delimitadores de subdiretório na criação de um objeto, mas não há subdiretórios verdadeiros no Cloud Storage. Em vez disso, um subdiretório no Cloud Storage faz parte do nome de arquivo do objeto.

Por exemplo, é possível pressupor que a criação de um objeto em somewhere/over/the/rainbow.mp3 armazenaria o arquivo rainbow.mp3 no subdiretório somewhere/over/the/. Em vez disso, o nome do objeto está definido como somewhere/over/the/rainbow.mp3.

Como usar a biblioteca de cliente do App Engine com o servidor do aplicativo de desenvolvimento

É possível usar a biblioteca de cliente com o servidor de desenvolvimento. No entanto, como não há emulação local do Cloud Storage, todas as solicitações de leitura e gravação de arquivos precisam ser enviadas pela Internet para um intervalo real do Cloud Storage.

Para usar a biblioteca de cliente com o servidor do aplicativo de desenvolvimento, execute dev_appserver.py com a sinalização --default_gcs_bucket_name [BUCKET_NAME], substitua [BUCKET_NAME] pelo nome do intervalo do Cloud Storage que você está usando.

Essa sinalização controla o intervalo que será retornado quando seu aplicativo chamar file.DefaultBucketName(ctx).

Como ler e gravar no Google Cloud Storage

Importações obrigatórias

O seguinte snippet mostra as importações de que você precisa para o acesso ao Cloud Storage por meio da biblioteca de cliente:

import com.google.appengine.tools.cloudstorage.GcsFileOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsInputChannel;
import com.google.appengine.tools.cloudstorage.GcsOutputChannel;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
import com.google.appengine.tools.cloudstorage.RetryParams;

Como especificar o intervalo do Cloud Storage

O seguinte snippet mostra uma maneira de permitir que o usuário especifique um nome de intervalo ao gravar um arquivo em um intervalo:

function uploadFile() {
  var bucket = document.forms["putFile"]["bucket"].value;
  var filename = document.forms["putFile"]["fileName"].value;
  if (bucket == null || bucket == "" || filename == null || filename == "") {
    alert("Both Bucket and FileName are required");
    return false;
  } else {
    var postData = document.forms["putFile"]["content"].value;
    document.getElementById("content").value = null;

    var request = new XMLHttpRequest();
    request.open("POST", "/gcs/" + bucket + "/" + filename, false);
    request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
    request.send(postData);
  }
}

O snippet pré-anexa o /gcs/ obrigatório ao intervalo e ao nome do arquivo fornecidos pelo usuário.

Como gravar no Cloud Storage

Para gravar um arquivo no Cloud Storage:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
  GcsFileOptions instance = GcsFileOptions.getDefaultInstance();
  GcsFilename fileName = getFileName(req);
  GcsOutputChannel outputChannel;
  outputChannel = gcsService.createOrReplace(fileName, instance);
  copy(req.getInputStream(), Channels.newOutputStream(outputChannel));
}

Esta amostra grava um novo arquivo no Cloud Storage ou, caso um arquivo com o mesmo nome já exista, ele o substitui. Isso é útil porque, depois de gravar um arquivo no Cloud Storage, você não poderá modificá-lo. Para alterar o arquivo, você precisa fazer as modificações em uma cópia do arquivo e substituir o arquivo anterior.

Como fazer a leitura do Cloud Storage

Para ler um arquivo do Cloud Storage:

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
  GcsFilename fileName = getFileName(req);
  if (SERVE_USING_BLOBSTORE_API) {
    BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
    BlobKey blobKey = blobstoreService.createGsBlobKey(
        "/gs/" + fileName.getBucketName() + "/" + fileName.getObjectName());
    blobstoreService.serve(blobKey, resp);
  } else {
    GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(fileName, 0, BUFFER_SIZE);
    copy(Channels.newInputStream(readChannel), resp.getOutputStream());
  }
}

Na linha que contém gcsService.openPrefetchingReadChannel, observe o uso da pré-busca. Isso armazena em buffer dados na memória e os pré-busca antes de ser obrigatório para evitar o bloqueio na chamada de leitura.

Como modificar objetos no Cloud Storage

Para modificar um objeto existente no Cloud Storage, grave o objeto modificado com o mesmo nome de arquivo que o original.

Como excluir objetos do Cloud Storage

No procedimento a seguir, você verá como excluir objetos em um aplicativo que armazena o nome completo do objeto do Cloud Storage (nome de arquivo) em um banco de dados do Cloud SQL:

final String bucket = "CLOUD-STORAGE-BUCKET"; // Cloud Storage bucket name
Map<String, String[]> userData = req.getParameterMap();

String[] image = userData.get("id"); // Grab the encoded image ID
String decodedId = new String(Base64.getUrlDecoder().decode(image[0])); // decode the image ID
int imageId = Integer.parseInt(decodedId);

// Grab the filename and build out a Cloud Storage filepath in preparation for deletion
try (PreparedStatement statementDeletePost = conn.prepareStatement(imageFilenameSql)) {
  statementDeletePost.setInt(1, imageId); // cast String to Int
  ResultSet rs = statementDeletePost.executeQuery(); // remove image record
  rs.next(); // move the cursor

  GcsFilename filename = new GcsFilename(bucket, rs.getString("filename"));
  if (gcsService.delete(filename)) {

    // Remove all records of image use in the blog
    // Use of foreign keys with cascading deletes will cause removal from blogpostImages table
    PreparedStatement statementDeleteImageRecord = conn.prepareStatement(deleteSql);
    statementDeleteImageRecord.setInt(1, imageId);
    statementDeleteImageRecord.executeUpdate();

    final String confirmation =
        "Image ID "
            + imageId
            + " has been deleted and record of its use in blog posts have been removed.";

    req.setAttribute("confirmation", confirmation);
    req.getRequestDispatcher("/confirm.jsp").forward(req, resp);
  } else {
    final String confirmation = "File marked for deletion does not exist.";

    req.setAttribute("confirmation", confirmation);
    req.getRequestDispatcher("/confirm.jsp").forward(req, resp);
  }
} catch (SQLException e) {
  throw new ServletException("SQL error", e);
}

O código acima decodifica o ID de imagem codificado em Base64 (em inglês) e recupera o nome de arquivo de uma imagem identificada pelo próprio image_id na tabela images. O nome de arquivo é convertido em um nome válido do Cloud Storage por meio de GcsFilename.

O arquivo é excluído do intervalo por meio de gcsService.delete. Por fim, ele remove o próprio registro de uso da tabela blogpostImage.

Próximas etapas

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Ambiente padrão do App Engine para Java 8