quinta-feira, 23 de maio de 2013

Playground - Workspace com implementações de exemplo

Playground

Pessoal, estou preparando uma área no meu dropbox com diversos exemplos de utilização de frameworks, códigos java em geral e etc. Resumindo, é o meu workspace do ecplise para testes! Lá vocês podem entrar e baixar como um zip de cada projeto.

Dentro de cada projeto, na raiz, existe um arquivo chamando README.TXT indicando o que aquela aplicação faz e as tecnologias que utiliza.

Qualquer dúvida sobre alguma implementação, pode me mandar um email, ou comentário aqui, que respondo assim que possível.

IMPORTANTE: Alguns projetos não estão totalmente completos e nem são exemplos magníficos de implementação. São testes! O próprio nome diz "Playground".

Endereço: Playground

[]s

sexta-feira, 17 de maio de 2013

Como configurar o Maven para um projeto web (Mavenize web project)

Neste post vou mostrar como criar um projeto web com maven. Para isso será necessário adicionar o plugin do Maven (m2eclipse), caso ainda não tenha.
Para fazer isso vá na menu "help" e depois "install new software". Selecionar "All available sites" em "work with", esperar carregar e na caixa inferior, e depois buscar por "maven integration for Eclipse". A instalação de plugins pode ter que ser realizada de diferentes maneiras dependendo da versão do eclipse instalada.

Em seguida, vamos criar o novo projeto, selecionando "Maven Project" no Eclipse.
Clique em “skip archetype selection” e defina o group id como: org.mycompany.lightshop. O group id é similar ao pacote padrão/base da aplicação. O artifact id, que é considerado o nome do projeto, deve ser definido como: lightshop. Na opção "packaging" selecione "war".

O archetype é considerado um template para projetos do Maven. Existem diversos exemplos de archetypes para diferentes frameworks. 

Depois que o projeto foi criado, você verá uma estrutura de diretórios diferente da estrutura padrão de um projeto web dinâmico. Nesta nova estrutura teremos um arquivo chamado "pom.xml" na raiz do projeto. Com ele é possível configurar todo o projeto, como por exemplo: adicionar dependências, gerenciar o ciclo de deploy, gerenciar repositórios, configurar propriedades e etc.
Abra o "pom.xml". Na aba dependencies, adicionar as dependências necessárias para o projeto. Aqui vou apenas adicionar a dependência para a JSTL, como exemplo:



Após adicionar as dependências, vá nas propriedades do projeto e depois em "Project Facets".
Clique em "convert to facet form" para converter um projeto para a forma "facetada" (configuração em partes). 
Selecione "Dynamic Web Project", para transformá-lo em um projeto web, e em "further configuration avaiable..." para terminar a configuração web do projeto.
Em "content directory" coloque "src/main/webapp" que é a diretório padrão/base com que o Maven gerencia as páginas web e não mais "WebContent" como estávamos acostumados. Depois dê OK.

Selecione essa pasta (src/main/webapp), botão direito -> build path -> source -> use as source folder, para definí-la pasta como uma pasta que contém código-fonte.

Crie uma página index.jsp em na pasta webapp. Adicione o projeto ao servidor, inicie e teste!

[]s

quinta-feira, 16 de maio de 2013

Criando templates usando o web.xml e fragmentos de JSPs (JSPFs)

Neste tópico vou abordar uma outra maneira de criar templates sem usar bibliotecas externas. Iremos utilizar apenas o web.xml e fragmentos de JSPs (JSPFs).

Inicialmente irei criar uma página HTML completa com cabeçalho, corpo e rodapé. Vou chamar essa página de index.jsp, salvá-la no WebContent com o código a seguir:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<link href="css/default.css" rel="stylesheet" type="text/css">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Página Principal</title>
</head>
<body>
    <div id="header">
        <h1>Título do web site</h1>
    </div>
    <div id="menu">
        <h2>Menu</h2>
        <ul>
            <li><a href="cadastrar.jsp">Cadastrar</a></li>
            <li><a href="listar.jsp">Listar</a></li>
        </ul>
    </div>
    <div id="content">
        <h1>Bem Vindo</h1>
    </div>
    <div id="footer">
        Informações do rodapé <br />copyright e etc.
    </div>
</body>
</html>

Vamos separar esse arquivo em 4. Um vai continuar se chamando index.jsp e criaremos outros três: header.jspf, menu.jspf e footer.jspf. Todos os arquivos criados devem ser copiados para uma nova pasta jspf dentro de WEB-INF.
O arquivo header.jspf deve conter apenas o código html correspondente ao início do arquivo index.jsp, ou seja:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<link href="css/default.css" rel="stylesheet" type="text/css">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Template Web Site</title>
</head>
<body>
    <div id="header">
        <h1>Título do web site</h1>
    </div

Já o arquivo footer.jspf deverá corresponder ao código ao final da página index.html:

<div id="footer">
    Informações do rodapé <br />copyright e etc.
</div>
</body>
</html>

O arquivo que irá conter o menu, irá se chamar menu.jspf e terá somente o código:

<div id="menu">
    <h2>Menu</h2>
    <ul>
        <li><a href="cadastrar.jsp">Cadastrar</a></li>
        <li><a href="listarjsp">Listar</a></li>
    </ul>
</div>

No arquivo do header temos um link para um arquivo chamado "default.css". O código correspondente ao arquivo é:

@CHARSET "UTF-8";

html,body {
    margin: 0;
    padding: 0;
}

body {
    height: 100%;
    font: 100%/1.4 Verdana, Arial, Helvetica, sans-serif;
}

#header {
    background: red;
    height: 120px;
    clear: both;
    text-align: center;
}

#footer {
    background: black;
    color: white;
    clear: both;
    text-align: center;
    padding: 10px;
}

#menu {
    padding: 10px;
    background: yellow;
    width: 120px;
    float: left;
    min-height: 400px;
}

#content {
    margin-left: 160px; /* menu width + 40 */
}

A página principal (aqui chamada de index.jsp) e qualquer outra página do projeto que você deseje que inclua o header, footer e menu, deve conter apenas a parte do corpo (<body>). Como exemplo segue o código para o index.jsp:

<div id="content">
    <h1>Bem Vindo</h1>
</div>

O código da index.jsp é correspondente ao código restante dos recortes de código executados posteriormente.

O web.xml que contém a configuração dos redirecionamentos do template para as páginas JSPFs deve ter o seguinte código:

<jsp-config>
    <jsp-property-group>
        <description>Configuracao do header, menu e footer do template</description>
        <url-pattern>*.jsp</url-pattern>
        <include-prelude>/WEB-INF/jspf/header.jspf</include-prelude>
        <include-prelude>/WEB-INF/jspf/menu.jspf</include-prelude>
<include-coda>/WEB-INF/jspf/footer.jspf</include-coda>
    </jsp-property-group>
</jsp-config>

A partir daí é só adicionar o projeto ao servidor e executar!

[]`s

sexta-feira, 5 de abril de 2013

Como enviar emails usando JavaMail

Neste post, vou apresentar como enviar um email através de uma servlet. 

Como de costume, criamos um projeto web dinâmico no eclipse, com, por exemplo, o nome "EnviarEmailServlet".

Para implementar este tutorial será necessária  a biblioteca JavaMail. A biblioteca pode ser encontrada no site da OracleNota: A menos que você esteja usando Java SE 6 ou mais recente, você também vai precisar da JavaBeans Activation Framework (JAF) que fornece o pacote javax.activation.

Depois de fazer o download, copie o arquivo mail.jar para a pasta lib do seu projeto.

O código-fonte da página index.jsp que contém o formulário para o envio do e-mail é:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Enviar E-Mail</title>
</head>
<body>

    <h1>Enviar E-Mail</h1>
    <form action="sendEmail.do" method="post">
        <table>
            <tr>
                <td>De</td>
                <td><input type="text" name="from" /></td>
            </tr>
            <tr>
            <tr>
                <td>Para</td>
                <td><input type="text" name="to" /></td>
            </tr>
            <tr>
                <td>Assunto</td>
                <td><input type="text" name="subject" /></td>
            </tr>
            <tr>
                <td>Mensagem</td>
                <td><textarea cols="25" rows="8" name="message"></textarea></td>
            </tr>
        </table>
        <br /> <input type="submit" value="Enviar" />
    </form>
</body>
</html>

Para receber estes parâmetros e enviar o e-mail devemos implementar uma servlet. Esta servlet vai se chamar SendEmail. Eu sobrescrevi o método init() da servlet para pegar os parâmetros necessários para o envio do email. A servlet deve conter o seguinte código:

package org.andvicoso.javamail.controller;

import java.io.IOException;
import java.util.Date;
import java.util.Properties;

import javax.mail.AuthenticationFailedException;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/sendEmail.do")
public class SendEmail extends HttpServlet {

    private static final String MSG_MIME_TYPE = "text/plain";

    private String host;
    private String port;
    private String login;
    private String password;

    @Override
    public void init() throws ServletException {
        ServletContext context = getServletContext();
        host = context.getInitParameter("host");
        port = context.getInitParameter("port");
        login = context.getInitParameter("login");
        password = context.getInitParameter("password");
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        String resultMsg = "E-Mail enviado com sucesso!";
        String from = request.getParameter("from");
        String to = request.getParameter("to");
        String subject = request.getParameter("subject");
        String message = request.getParameter("message");

        try {
            Properties props = getSMTPProperties();

            Authenticator auth = new SMTPAuthenticator(login, password);

            Session session = Session.getInstance(props, auth);

            MimeMessage msg = createMessage(from, to, subject, message, session);

            Transport.send(msg);
        } catch (AuthenticationFailedException ex) {
            resultMsg = "Falha na autenticação";
        } catch (AddressException ex) {
            resultMsg = "E-Mail de destino inválido!";
        } catch (MessagingException ex) {
            resultMsg = ex.getMessage();
            //ex.printStackTrace();//descomentar para debug
        }

        request.setAttribute("message", resultMsg);

        RequestDispatcher dispatcher = request
                .getRequestDispatcher("result.jsp");
        dispatcher.forward(request, response);
    }

    private MimeMessage createMessage(String from, String to, String subject,
            String message, Session session) throws MessagingException {
        MimeMessage msg = new MimeMessage(session);
        msg.setText(message);
        msg.setSubject(subject);
        msg.setFrom(new InternetAddress(from));
        msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
        msg.setContent(message, MSG_MIME_TYPE);
        msg.setSentDate(new Date());

        return msg;
    }

    private Properties getSMTPProperties() {
        Properties props = new Properties();
        props.setProperty("mail.host", host);
        props.setProperty("mail.transport.protocol", "smtp");
        // Comente essas 4 linhas abaixo se deseja usar o SSL
        props.setProperty("mail.smtp.auth", "true");
        props.setProperty("mail.smtp.port", port);
        props.setProperty("mail.smtp.ssl.trust", host);
        props.setProperty("mail.smtp.starttls.enable", "true");
        // Descomente essas tres linhas abaixo se deseja usar SSL
//         props.setProperty("mail.smtps.auth", "true");
//         props.setProperty("mail.smtps.port", port);
//         props.setProperty("mail.smtps.ssl.trust", host);
        //Descomente a linha abaixo se deseja informações de debug
//        props.put("mail.debug", "true");

        return props;
    }

    private class SMTPAuthenticator extends Authenticator {
        private String login;
        private String password;

        public SMTPAuthenticator(String login, String password) {
            this.login = login;
            this.password = password;
        }

        public PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(login, password);
        }
    }
}

Se você preferir para criptografar suas conexões com SSL, então certifique-se de suas propriedades incluam SMTPS em vez de SMTP e que a propriedade mail.smtps.ssl.trust seja definida.

Para enviar e-mail para vários destinatários apenas adicionar mais endereços através do método  addRecipient.

Para anexar um ou mais arquivos no e-mail devemos alterar o tipo do conteúdo do e-mail para MimeMultipart na função createMessage.

Segue o código para isso:

    // cria uma parte do corpo da mensagem como sendo o texto
    MimeBodyPart messageBodyPart = new MimeBodyPart();
    // preenche a mensagem do e-mail
    messageBodyPart.setText(message);

    Multipart multipart = new MimeMultipart();
    multipart.addBodyPart(messageBodyPart);

    // a segunda parte do corpo é o anexo
    messageBodyPart = new MimeBodyPart();
    DataSource source = new FileDataSource(fileAttachment);//esse fileAttachment é caminho para o arquivo!
    messageBodyPart.setDataHandler(new DataHandler(source));
    messageBodyPart.setFileName(fileAttachment);
    multipart.addBodyPart(messageBodyPart);

    // seta o conteúdo da mensagem como sendo o multipart criado (texto+arquivo)
    msg.setContent(multipart);//ao invés de msg.setContent(message, MSG_MIME_TYPE);

Uma outra ideia interessante é usar um arquivo properties para armazenar as propriedades do servidor SMTP.

Para armazenar os parâmetros necessários para a conexão com o servidor SMTP eu adicionei parâmetros de contexto no web.xml. Desta maneira:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>JavaMail</display-name>

    <!-- SMTP settings -->
    <context-param>
        <param-name>host</param-name>
        <param-value>smtp.gmail.com</param-value>
        <!-- ou outro se não for gmail -->
    </context-param>

    <context-param>
        <param-name>port</param-name>
        <param-value>587</param-value>
        <!-- O padrão é 25. Podemos usar a 26 se o ISP bloquear a 25 -->
    </context-param>

    <context-param>
        <param-name>login</param-name>
        <param-value>abc@gmail.com</param-value>
        <!-- ou outro email se não for gmail -->
    </context-param>

    <context-param>
        <param-name>password</param-name>
        <param-value>abc</param-value>
        <!-- colocar a senha do email -->
    </context-param>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Altere os valores parâmetros no web.xml para os seus valores e depois é só testar!

Para este exemplo também é possível encontrar um projeto exemplo no meu dropbox.

Até o próximo post!

[]`s

segunda-feira, 1 de abril de 2013

Criando um template para web site usando Sitemesh 3

Criando um template para web site usando Sitemesh 3


Este post vai apresentar como implementar um template para web site usando a biblioteca Sitemesh 3.

Para este exemplo, vou criar um projeto web dinâmico no Eclipse. O nome pode ser TemplateSitemesh3 por exemplo.

Para implementar o exemplo precisamos da biblioteca sitemesh na versão 3, que é encontrada em:
Baixe o zip da biblioteca, descompacte, procure o arquivo sitemesh-3.0-alpha-2.jar dentro do diretório e copie ele para a pasta lib dentro de WebContent do seu projeto.

Depois de copiar a biblioteca, crie um arquivo chamado sitemesh3.xml dentro do diretório WEB-INF. Este arquivo serve para configurar qual o arquivo que será considerado o template da aplicação (chamado de decorator) e quais arquivos não vão ser considerados pelo template, como por exemplo, arquivos CSS, imagens, JavaScripts e etc. Neste exemplo eu vou usar como decorator o arquivo /WEB-INF/decorators/main.jsp e vou excluir da "decoração" os arquivos de estilos, imagens e JavaScripts. O código do arquivo sitemesh3.xml fica assim:


<sitemesh>
    <mapping decorator="/WEB-INF/decorators/main.jsp" />
    <!-- Excluir o caminho da decoracao. -->
    <mapping path="/images/*" exclue="true" />
    <mapping path="/javascript/*" exclue="true" />
    <mapping path="/styles/*" exclue="true" />
</sitemesh>

Para mais informações sobre filtros, acesse o link: da caelum ou da oracle. Ou ainda o livro "Use a cabeça: Servlets e JSP".

Em seguida, devemos definir no web.xml um filtro do Sitemesh que vai tratar todas as requisições e aplicar o template sobre elas.

    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>

Todas as páginas html e jsp que serão criadas neste projeto deverão passar pelo filtro do Sitemesh que irá "decorar" as páginas com o decorator. Assim todas essas páginas não deverão conter (repetir) as partes comuns do template, tais como: header, footer, menu e etc.

Agora vou mostrar um código de exemplo para o arquivo main.jsp, que é o nosso decorator. Ele deve estar localizado no diretório /WEB-INF/decorators/ . Segue o código:


<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" trimDirectiveWhitespaces="true" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" view="width=device-width, initial-scale=1.0">
    <meta http-equiv="Content-Type" view="text/html; charset=utf-8" />
    <meta http-equiv="Content-Language" view="pt-BR" />
    
    <link href="styles/default.css" rel="stylesheet">
    
    <title>TemplateSitemesh3 - <sitemesh:write property="title" /></title>
    
    <sitemesh:write property="head" />
</head>
<body>
    <jsp:include page="template/header.jsp" />
    
    <sitemesh:write property="body" />

    <jsp:include page="template/footer.jsp" />
    <!-- javascript -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="scripts/jquery/jquery.js"></script>
</body>
</html>

Neste exemplo, eu mostro como referenciar um arquivo CSS e um JavaScript. Lembrando que no seu projeto devem ser alterados os caminhos para os arquivos relativos correspondentes. Vale o mesmo para os comandos jsp:include onde eu incluo o header e footer, que não são apresentados neste exemplo, mas ambos contêm código JSP comum.

A linha de código <sitemesh:write property="head" />  indica que ao "decorar" a página o sitemesh vai copiar o cabeçalho (head) da página corrente e colar na página "decorada" final. A mesma coisa acontece com o comando <sitemesh:write property="body" />, só que adicionando o conteúdo do corpo (body) da página corrente. Já a instrução <sitemesh:write property="title" /> copia o título da página sendo decorada para a tag title do head da página final.

Bom, acho que é só! Daqui pra frente é só criar as suas páginas de view (HTMLs e JSPs) normalmente e testar. É possível baixar o projeto do eclipse de exemplo do meu dropbox.
Qualquer dúvida ou comentário, é só falar!

quinta-feira, 28 de fevereiro de 2013

Como fazer upload e download de arquivos usando Servlet e JSP

Este post vai apresentar como implementar um upload e download de arquivos usando Servlets, JSP, JSTL, EL e o padrão MVC.

Para este exemplo, vou criar um projeto web dinâmico no Eclipse. Eu não vou especificar um banco de dados, o código deve funcionar com qualquer banco relacional.

Para implementar o exemplo precisamos de quatro bibliotecas adicionais, sendo duas da Apache:
E para usar o JSTL precisamos de mais duas bibliotecas adicionais, a API e a implementação. Ambos podem ser encontrados no link:
Baixe todas as bibliotecas e adicione na pasta lib dentro de WebContent. A pasta lib também deve conter o conector para o banco de dados utilizado.

Upload

A página de envio deve conter um formulário com os campos necessários e mais um campo de input type file para o upload. No exemplo a seguir, vou mostrar o formulário com apenas dois campos: input type="file" e outro input type="text".

<form action="upload.do" enctype="multipart/form-data" method="post">
    <label for="desc">Descrição:</label>
    <input type="text" name="desc" id="desc"><br/>
    <label for="file">Arquivo:</label>
    <input type="file" name="file" id="file"><br/>
    <input type="submit" value="Enviar"/>
</form>

A servlet que vai receber os dados deste formulário deve conter o código:

public class UploadFileServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        DiskFileItemFactory dfif = new DiskFileItemFactory();
        ServletFileUpload sfu = new ServletFileUpload(dfif);

        if (!ServletFileUpload.isMultipartContent(req)) {
              // tratar erro
        }

        try {
            List items = sfu.parseRequest(req);
            //a posicao 0 corresponde ao primeiro campo input do formulario (descricao)
            FileItem descFI = (FileItem) items.get(0);
            String desc = descFI.getString();
            //a posicao 1 corresponde ao segundo campo input do formulario (arquivo)
            FileItem fileFI = (FileItem) items.get(1);
            byte[] bytes = read(fileFI);
            //Não é o File do Java. É um JavaBean apresentado a seguir
            FileBean filebean = new FileBean();                   
            filebean.setDescription(fileFI.getDesc());
            filebean.setName(fileFI.getName());
            filebean.setData(bytes);

            FileDao fdao = new FileDaoImpl();
            fdao.add(filebean);
        } catch (FileUploadException e) {
            // tratar erro
        }

        resp.sendRedirect("list.jsp");
    }

    private byte[] read(FileItem fi) throws IOException{
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        InputStream is = fi.getInputStream();
        int read = 0;
        final byte[] b = new byte[1024];

        while ((read = is.read(b)) != -1) {
            out.write(b, 0, read);
        }
        return out.toByteArray();
    }
}

Para verificar se um FileItem é um campo do comum formulário ou um campo (input) do tipo file, podemos usar o método: fileItem.isFormField().

Não se esqueça de mapear a sua servlet (UploadFileServlet)! Pode ser mapeada usando anotações (API Servlet 3.x) ou com o bom e velho deployment descriptor (web.xml). Segue o trecho do web.xml correspondente:


    <servlet>
        <servlet-name>UploadFileServlet</servlet-name>
        <servlet-class>controller.user.file.UploadFileServlet</servlet-class>
    </servlet>

   <servlet-mapping>
        <servlet-name>UploadFileServlet</servlet-name>
        <url-pattern>/upload.do</url-pattern>
    </servlet-mapping>

No código acima a classe da servlet se encontra no pacote controller.user.file , altere para o caminho correto dentro da sua aplicação.

A servlet UploadFileServlet faz uso da classe JavaBean FileBean, com o código:

public class FileBean {
    private String description;
    private String name;
    private Long id;
    private byte[] data;

    public FileBean() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Long getId() {
        return id;
    }

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

    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }
}

Para armazenar o código no banco de dados temos que ter uma tabela que corresponda ao arquivo. Essa tabela deve conter um varchar para o nome, outro para a descrição, um campo blob para o arquivo (bytes) e um campo id para identificar cada arquivo adicionado. Devemos setar esse último campo como autoincrement (MySQL).

O código da DAO que armazena os dados, lista e busca o JavaBean (FileBean) no banco de dados é:

     public boolean add(FileBean filebean) {
        int added = 0;
        try {
            Connection con = ConnectionFactory.createConnection();
            if (con != null) {
                String sql = "INSERT INTO file (name, description, data) VALUES (?, ?, ?)";
                PreparedStatement st = con.prepareStatement(sql);
                st.setString(1, filebean.getName());
                st.setString(2, filebean.getDescription());
                st.setBytes(3, filebean.getData());
                added = st.executeUpdate();
                st.close();
                con.close();
            }
        } catch (SQLException e) {
            //tratar erro
        }

        return added > 0;
    }

     public List list() {
        List files = new ArrayList();
        try {
            Connection con = ConnectionFactory.createConnection();
            if (con != null) {
                String sql = "SELECT * FROM file";
                Statement st = con.createStatement();
                ResultSet rs = st.execute(sql);

                while(rs.next()){
                    FileBean filebean = createFileBean(rs);
                    files.add(filebean);
                }

                st.close();
                con.close();
            }
        } catch (SQLException e) {
            //tratar erro
        }

        return files;
    }

    public FileBean getFile(long id) {
        FileBean filebean = null;
        try {
            Connection con = ConnectionFactory.createConnection();
            if (con != null) {
                String sql = "SELECT * FROM file WHERE id=?";
                PreparedStatement st = con.prepareStatement(sql);
                st.setLong(1, id);
                ResultSet rs = st.executeQuery();
                if (rs.next()) {
                    filebean = createFileBean(rs);
                }
                rs.close();
                st.close();
                con.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return file;
    }

    private FileBean createFileBean(ResultSet rs) throws SQLException {
        FileBean filebean = new FileBean();
        filebean.setData(rs.getBytes("data"));
        filebean.setId(rs.getLong("id"));
        filebean.setName(rs.getString("name"));
        filebean.setDescription(rs.getString("description"));

        return filebean;
    }

Os métodos da DAO fazem chamada a um método da classe ConnectionFactory que apenas cria e retorna uma conexão ao banco de dados. Daqui já podemos executar pra testar!

Download

Para o download vou mostrar um trecho de código de um JSP (list.jsp) que lista os arquivos do banco de dados e ao clicar sobre um arquivo o download é iniciado. Este código faz uso do JSTL e da EL. Se for uma imagem ele mostra um thumbnail ao invés do botão de download.


    <table>
        <thead>
            <tr>
                <th align="center"><b>Nome</b></th>
                <th align="center"><b>Descrição</b></th>
                <th align="center"><b>Arquivo</b></th>
            </tr>
        </thead>

        <tbody>
            <c:forEach var="file" items="${items}">
                <c:set value="${fn:endsWith(file.name, 'jpg') or fn:endsWith(file.name, 'png')}" var="isImage"/>
                <tr>
                    <td>${file.name}</td>
                    <td>${file.description}</td>
                    <c:if test="${isImage}">
                        <td><img width="50" height="50" src="download.do?id=${file.id}"/></td>
                    </c:if>
                    <c:if test="${not isImage}">
                        <td><a href="download.do?id=${file.id}">Baixar</a></td>
                    </c:if>
                </tr>
            </c:forEach>
        </tbody>
    </table>

A servlet que busca os arquivos na base de dados tem o código:

  public class ListFilesServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        FileDao dao = new FileDaoImpl();
        List<FileBean> files = dao.list();

        req.setAttribute("items", dao.list());
    
        RequestDispatcher rd = pReq.getRequestDispatcher("list.jsp");
        rd.forward(req, resp);
    }
}

A classe da servlet responsável pelo download tem o código:

public class DownloadFileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String idStr = req.getParameter("id");
        if(idStr == null){     
           //redirect to home or other
        }

        FileDao dao = new FileDaoImpl();
        Long id = Long.parseLong(idStr);
        FileBean filebean = dao.getFile(id);

        if (file != null) {
            ServletContext context = getServletConfig().getServletContext();
            String mimetype = context.getMimeType(filebean.getName());

            resp.setContentLength(filebean.getData().length);
            resp.setContentType(mimetype);//resp.setContentType("image/jpeg");

            resp.setHeader("Content-Disposition", "attachment; filename=\"" +    filebean.getName() + "\"");

            OutputStream out = resp.getOutputStream();
            InputStream in = new ByteArrayInputStream(filebean.getData());
            byte[] buffer = new byte[4096];
            int length;
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
            in.close();
            out.flush();
        }
    }
}

Elas também devem ser mapeadas, tal como no exemplo a seguir:

<servlet>
        <servlet-name>DownloadFileServlet</servlet-name>
        <servlet-class>controller.user.file.DownloadFileServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DownloadFileServlet</servlet-name>
        <url-pattern>/download.do</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>ListFilesServlet</servlet-name>
        <servlet-class>controller.user.file.ListFilesServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ListFilesServlet</servlet-name>
        <url-pattern>/list.do</url-pattern>
    </servlet-mapping>

Acho que é isso. Se tiver algum erro, melhoria ou dificuldade na implementação é só avisar!

Happy coding!
[]`s

Popular Posts

StackOverflow Profile


profile for andvicoso at Stack Overflow, Q&A for professional and enthusiast programmers