Pular para o conteúdo principal

Appengine - Armazenando arquivos no Datastore

Já faz algum tempo em que eu tenho utilizado o Google App Engine para desenvolver meus projetos pessoais em Java, que atualmente o maior é o e-Karros. Apesar dos vários benefícios da plataforma, existem alguns pontos que trazem dificuldades ao desenvolvimento. A dificuldade mais recente que encontrei foi o armazenamento de arquivos no Datastore do App Engine. No meu caso específico foi o armazenamento de imagens. Após um bom tempo de pesquisa na net encontrei a solução abaixo.

Para podermos armazenar arquivos precisamos utilizar o tipo de dado Blob, através da API Blobstore. Bom, em primeiro lugar temos que criar nossa classe que irá armazenar o arquivo, que neste caso pode ser uma imagem:
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Blob;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Imagem {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private Blob imagem;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setImagem(Blob imagem) {
        this.imagem = imagem;
    }

    public Blob getImagem() {
        return imagem;
    }
       
    public void adiciona() throws Exception {
        new ImagemDAO().adiciona(this);
    }

    public ImagemCarro buscaImagem(Long id) {
        return new ImagemDAO().buscaImagem(id);
    }
}

Vamos supor que nossa classe ImagemDAO já esteja implementada.

Após isso temos nossa classe Servlet que irá receber o arquivo que foi feito upload, tratá-lo e chamar o método de gravação. Para se "caputrar" o arquivo e converter para o formato da classe Blob (array de bytes), precisando da biblioteca Commons IOUtils, da Apache.

import org.apache.commons.io.IOUtils;

public class ArmazenaImagem extends HttpServlet {
 
    public void doPost(HttpServletRequest req, HttpServletResponse resp) {
        try {
            InputStream imgStream = req.getInputStream();
            Blob imageBlob = new Blob(IOUtils.toByteArray(imgStream));

            Imagem img = new Imagem();
            img.setImagem(imageBlob);

            img.adiciona();

            resp.setStatus(HttpServletResponse.SC_OK);
            resp.setContentType("text/plain");
            resp.getWriter().println( "{success: true}" );

        } catch (Exception e {
            e.printStackTrace();
            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            resp.setContentType("text/plain");
            resp.getWriter().println( "{success: false}" );
        }
    }
}
Como podemos ver, é bem simples gravar um arquivo no Datastore.

Aqui uma pausa para explicar a linha resp.getWriter().println( "{success: true}" ); e seu equivalente no bloco catch. É um requerimento da plugin JavaScript Ajax Upload, o melhor que encontrei para esse trabalho ;-). A utilização de um plugin para upload foi a única forma que eu encontrei para enviar o arquivo ao servidor utilizando AJAX, porém ainda estou a procura de uma outra forma.

A recuperação da imagem é igualmente simples. Abaixo segue o servlet:
public class BuscaIMagemServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
        try {
            resp.setContentType("image/jpeg");
            resp.setHeader("Content-Disposition","inline");
   
            Long id = Long.parseLong( req.getParameter( "id" ) );
            Imagem imagem = new Imagem().buscaImagem(ido);
   
            Blob blobImagem = imagem.getImagem();
            byte[] btImg = blobImagem.getBytes();
   
            resp.getOutputStream().write(btImg);
   
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
É somente isso. Ao recuperarmos a imagem do Datastore precisamos transformá-la em um array de bytes.

Nesse caso usei o campo id para recuperar uma imagem específica. Para exibir a imagem na página precisamos inserir a tag img em nossa página:
Sem Imagem
Onde '/buscaImagem?id=1' é o nosso servlet que recupera a imagem, e id=1 é o parâmetro para buscarmos uma imagem específica (o número 1 q coloquei é só um exemplo).

Espero que este breve tutorial sirva para que outras pessoas não fiquem batendo tanta cabeça quanto eu ;-).

Comentários

Gustavo disse…
Parabéns, muito bom.
Meu problema atual era somente a recuperação do arquivo na Nuvem.
Estava convertendo errado o tipo Blob.
Muito obrigado, ótimo post ;)
Tiago dos Santos disse…
Gustavo, obrigado pelo comentário. É bom saber que o blog está sendo útil.
Gustavo disse…
Opa Tiago,
você pode me ajudar?
Estou com um problema...
Estou tentando listar vários arquivos que realizei upload no app engine...
Só que quando chamo o list no jsp... e tento lista-los
aparece o seguinte erro:

No meta data for modelo.Arquivo. Perhaps you need to run the enhancer on this class?
Tiago dos Santos disse…
Gustavo, essa mensagem eu nunca vi, mas se poder, me envie mais detalhes por e-mail (tiago_stos(at)yahoo . com . br).
Gustavo disse…
Opa Tiago,
Consegui resolver, este erro estava aparecendo porque não utilizei um modelo na classe arquivo. @PersistenceCapable.
Mais obrigado pela atenção, abraço.

Postagens mais visitadas deste blog

Tutorial Bacula: Iniciando o serviço e operando o sistema

Chegamos a última parte do tutorial. Aqui vou falar sobre como iniciar o serviço e operar o servidor Bacula. Não veremos tudo, mas somente o essencial para que, a partir deste ponto, quem leu o tutorial possa configurar e operar o sistema, mesmo que basicamente. Então, mãos-à-obra. Para iniciarmos o sistema, os comando são: # bacula-dir # bacula-fd # bacula-sd que iniciam o Director Daemon, o File Daemon e o Storage Daemon respectivamente. Caso exista algum erro no arquivo de configuração, irá aparecer uma mensagem apontando qual o erro e o serviço não irá subir. Se for esse o caso, corrija os erros e execute o comando novamente. Se não exibiu mensagem de erro alguma, então o sistema deve estar no ar. Caso queira confirmar, digite o comando: # ps aux | grep bacula Esse comando exibirá todos os processos que estejam rodando e que contenham a sequência "bacula". Se tudo deu certo, vamos operar o sistema. Para operar o sistema, utilizamos o console do Bacula, no caso o bconsole....

Linux de bolso

Comecei a faculdade (ciência da computação, escolha meio obvia) esse ano e logo de cara fiquei sabendo que iriamos utilizar linux nos computadores da faculdade, o que achei muito bom. Mas chegando no laboratório de informática vi que estava instalado o CentOS , se não me engano é baseado no Red Hat Enterprise (e não Fedora, como tinha postado antes). Até ai tudo bem, nada contra em usar uma distribuição que não conheço, o problema foi quando percebi que o sistema estava da forma como foi instalado, sem nenhuma configuração extra. Primeiro percebi que a resolução estava em 800x600, e não havia como mudar a resolução para 1024x768, que julgo ser ideal de ser usada. Isso até que podia ser suportado, mas ao tentar usar meu pendrive ele não foi reconhecido automaticamente, ou pelo menos não foi montado automaticamente, e depois tentando monsta-lo manualmente, o sistema só dá permição para o root montar dispositivos, e a senha do root, como não podia ser diferente, não temos acesso. Com es...

Tutorial sobre Bacula

Conforme prometido irei postar aqui um tutorial sobre Bacula . Ou melhor, uma série de post que juntos se tornarão um tutorial mais completo. Decidi fazer dessa forma, dividindo em várias partes, pois o assunto é muito complexo e grande, e colocar tudo em um post só seria complicado. Quando terminar tentarei unir o tutorial em um só, e para ficar melhor gostaria de receber dicas, criticas, correções... nos comentários para fazer devidas alterações. Para começar, vamos a uma introdução sobre o que exatamente é o Bacula e suas principais funções. Essa parte já esta pronta, somente irei fazer correções e algumas alterações para melhora-lo, e, hoje ou no máximo amanhã, irei postar aqui. Mas antes de ler (ou depois de ler, você é quem decide), sugiro que leiam os seguintes links: Site oficial do Bacula Conceitos importantes sobre backup - É sobre um sistema de backup diferente, mas os conceitos são os mesmo do Bacula. Aqui você encontra o menu completo desse outro sistema de backup. Tut...