31 de outubro de 2009

Recebendo e-mails e anexos com JavaMail

Bom pessoal, precisei fazer uma pequena aplicação para receber e-mails e anexos no caso XMLs, só que vi somente artigos e tutoriais que apenas ensinavam como enviar o e-mail, o site do JavaMail, também não está tão legal, algumas coisas como salvar os anexos em pastas não estava tão bom, então resolvi pegar o código que fiz em casa e hoje está adaptado para rodar aqui no trabalho e vou postá-lo:
Ajuda no GUJ que obtive enquanto fazia: http://www.guj.com.br/posts/list/15/142839.java#771656
Download do código no github aqui.

A solução, ficou assim:

Classe de Constantes:

public class Constantes { public static final String PASTA_XML = pasta onde o XML será salvo em seu computador;
public static final String IMAP = "imap";
public static final String HOST = Seu host;
public static final int PORTA = Sua porta;
public static final String ARQUIVO_MSG = Arquivo de mensagens do seu e-mail;
public static final String LOGIN = Seu login;
public static final String SENHA = Sua senha;
public static final String PASTA_PRINCIPAL = "Inbox";
public static final String PASTA_BACKUP = pasta caso você copie os e-mails da principal como fiz;

}

Classe ReadEmails

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;

import javax.mail.AuthenticationFailedException;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.FolderClosedException;
import javax.mail.FolderNotFoundException;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Part;
import javax.mail.ReadOnlyFolderException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.StoreClosedException;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;

/**
* @author Eduardo Bregaida
*
*/
public class ReadEmails {
private Store store = null;
private Folder folder = null;
private Message message = null;
private Message[] messages = null;
private Object msgObj = null;
private String sender = null;
@SuppressWarnings("unused")
private String subject = null;
private Multipart multipart = null;
private Part part = null;
private String contentType = null;

public ReadEmails() throws MessagingException {
processMail();
}

/**
* Processa o e-mail
*
*/
public void processMail() throws MessagingException {
try {
store = conexaoServidorEMail();
folder = getPastaCaixaEntrada(store);
messages = folder.getMessages();

for (int messageNumber = 0; messageNumber < messages.length; messageNumber++) {
message = messages[messageNumber];
msgObj = message.getContent();

// Determine o tipo de email
if (msgObj instanceof Multipart) {

subject = message.getSubject();
multipart = (Multipart) message.getContent();

for (int i = 0; i < multipart.getCount(); i++) {

part = multipart.getBodyPart(i);
// pegando um tipo do conteúdo
contentType = part.getContentType();

String fileName2 = part.getFileName();
if(fileName2 != null) {
System.out.println(messageNumber + " " + fileName2 + " | " + message.getSubject());
}
fileName2 = null;

// Tela do conteúdo
if (contentType.startsWith("text/plain")) {
} else {
String fileName = part.getFileName();
@SuppressWarnings("unused")
Message[] mensagensXML = separaMensagensXML(i, fileName);

}
}
} else {
sender = ((InternetAddress) message.getFrom()[0]).getPersonal();
if (sender == null) {
sender = ((InternetAddress) message.getFrom()[0]).getAddress();
}
// Get the subject information
subject = message.getSubject();
}
}
// Fecha a pasta
folder.close(true);
// Histório de mensagens
store.close();
System.out.println("Terminado");
} catch (AuthenticationFailedException e) {
store.close();
e.printStackTrace();
} catch (FolderClosedException e) {
store.close();
e.printStackTrace();
} catch (FolderNotFoundException e) {
store.close();
e.printStackTrace();
} catch (NoSuchProviderException e) {
store.close();
e.printStackTrace();
} catch (ReadOnlyFolderException e) {
store.close();
e.printStackTrace();
} catch (StoreClosedException e) {
store.close();
e.printStackTrace();
} catch (Exception e) {
store.close();
e.printStackTrace();
}
}

/**
* @param i
* @param fileName
* @return
* @throws MessagingException
* @throws IOException
*/
private Message[] separaMensagensXML(int i, String fileName) throws MessagingException, IOException {
Message[] mensagensXML = folder.getMessages();;
if (fileName != null) {
int tamanhoString = fileName.length() - 3;
for (int a = 0; a < messages.length; a++) {
if (fileName.substring(tamanhoString).equals("xml")) {
mensagensXML[a] = message;
}
}
}
// Recebendo o nome do arquivo
@SuppressWarnings("unused")
String fileName2 = validarXML(part, store, folder, mensagensXML, i);
return mensagensXML;
}

/**
* @param messages
* @param i
* @throws MessagingException
*/
private void excluirMensagemInbox(Message[] messages, int i) throws MessagingException {
@SuppressWarnings("unused")
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
messages[i].setFlag(Flags.Flag.DELETED, true);
}

/**
* Envia os arquivos da pasta princiál para a pasta reserva
*
* @param store
* @param folder
* @param messages
* @throws MessagingException
*/
private boolean enviaArquivoPastaAuxiliar(Store store, Folder folder, Message[] messages, int i) throws MessagingException {
return false;
Folder folderAux;
folderAux = getPastaAuxiliar(store);
folder.copyMessages(messages, folderAux);
folderAux.close(true);
excluirMensagemInbox(messages, i);
}

/**
* Recebe o anexo e valida se é um XML, se sim ele salva o arquivo em uma
* pasta
*
* @param part
* @return
* @throws MessagingException
* @throws IOException
*/
private String validarXML(Part part, Store store, Folder folder, Message[] messages, int i) throws MessagingException, IOException {
String fileName = part.getFileName();
if (fileName != null) {
int tamanhoString = fileName.length() - 3;
if (!fileName.substring(tamanhoString).equals("xml")) {
return fileName;
} else {
String disposition = part.getDisposition();
if ((disposition != null) && ((disposition.equals(Part.ATTACHMENT) || (disposition.equals(Part.INLINE))))) {
salvarArquivo(part);
enviaArquivoPastaAuxiliar(store, folder, messages, i);
}
}
}
return fileName;
}

/**
* Salva o arquivo em uma pasta
*
* @param part
* @throws MessagingException
* @throws IOException
*/
private void salvarArquivo(Part part) throws IOException, MessagingException {
FileOutputStream fileOutputStream = new FileOutputStream(Constantes.PASTA_XML + part.getFileName());
Object obj = part.getContent();
if (obj instanceof InputStream) {
InputStream is = (InputStream) obj;
int ch = -1;
while ((ch = is.read()) != -1) {
fileOutputStream.write(ch);
}
}
}

/**
* Acessa a Caixa de Entrada (Inbox)
*
* @param store
* @return
* @throws MessagingException
*/
private Folder getPastaCaixaEntrada(Store store) throws MessagingException {
Folder folder;
folder = store.getFolder(Constantes.PASTA_PRINCIPAL);
folder.open(Folder.READ_WRITE);
return folder;
}

/**
* Acessa a Pasta Auxiliar
*
* @param store
* @return
* @throws MessagingException
*/
private Folder getPastaAuxiliar(Store store) throws MessagingException {
Folder folder;
folder = store.getFolder(Constantes.PASTA_BACKUP);
folder.open(Folder.READ_WRITE);
return folder;
}

/**
* Autenticação e conexão com o Servidor de e-mail
*
* @return
* @throws NoSuchProviderException
* @throws MessagingException
*/
private Store conexaoServidorEMail() throws NoSuchProviderException, MessagingException {
Session session;
Store store;
Properties prop = new Properties();
session = Session.getInstance(prop);
URLName url = new URLName(Constantes.IMAP, Constantes.HOST, Constantes.PORTA, Constantes.ARQUIVO_MSG, Constantes.LOGIN, Constantes.SENHA);
store = session.getStore(url);
store.connect();

return store;
}
}

Classe Principal (Main):

import javax.mail.MessagingException;

/**
* @author Eduardo Bregaida
*
*/
public class PrincipalNfe {

public static void main(String[] args) {
@SuppressWarnings("unused")
ReadEmails readMail =null;
try {
readMail = new ReadEmails();
} catch (MessagingException e) {
e.printStackTrace();
}

}

}

import javax.mail.MessagingException;
/**
* @author Eduardo Bregaida
*
*/
public class PrincipalNfe {
public static void main(String[] args) {
@SuppressWarnings("unused")
ReadEmails readMail =null;
try {
readMail = new ReadEmails();
} catch (MessagingException e) {
e.printStackTrace();
}
}
}

Manifest:

Manifest-Version: 1.1
Main-Class: PrincipalNfe
Class-Path: lib-dep/activation.jar
lib-dep/dsn.jar
lib-dep/imap.jar
lib-dep/mail.jar
lib-dep/mailapi.jar
lib-dep/pop3.jar
lib-dep/smtp.jar

45 comentários:

jeferson disse...

Parabéns pela equipe do Java Anywhere, e principalmente ao eduardo.

Recebi ajuda do amigo, e automatizei a rotina de recebimento de DANFE (xml) com está função.

Muito Obrigado!

Eduardo Bregaida disse...

Obrigado pelo comentário Jeferson, fico feliz por ter ajudado, estamos aqui para passar sempre o nosso conhecimento para quem precisar.

Abraços

Edlaine disse...

Parabens pelo codigo,porem estou com dificuldade para realizar a conexao..

Eduardo Bregaida disse...

Edlaine, obrigado, disponibilizei o fonte do exemplo dentro do meu GithHub, fique a vontade para baixar e tentar odificá-lo para seu projeto.

Edlaine disse...

Muito bommm...Obrigadoo

Edlaine disse...

Entao Eduardo estou com problema na pasta de backup no caso poderia ser qualquer pasta que eu queria fazer uma copia do arquvo "uma pasta que eu tenha no meu email"?

Eduardo Bregaida disse...

A pasta de backup é a pasta que você irá copiar os emails que serão excluídos da inbox, pode ser qualquer nome, basta criar uma pasta no seu email =)

Edlaine disse...

Eduardo estou com um problema coloco o nome da pasta de backup porem nao esta dando certo, por acaso tem uma maneira diferente para colocar o nome?.Tenho que colocar o caminho?p
Pode me explicar?

Eduardo Bregaida disse...

Olá Edlaine, a pasta Backup ela deve ficar no nível da pasta Inbox, ela serve para quando o email é lido, ele vá para essa pasta e a mensagem original da inbox seja excluída.

Espero ter ajudado.

Felipe Carvalho disse...

Eduardo,

Muito obrigado por compartilhar seu conhecimento! Você está ajudando muito a comunidade

Abraços
Felipe

Eduardo Bregaida disse...

Obrigado Felipe, fico feliz em ter ajudado.

Helcio disse...

Eduardo muito legal esse projeto, mas como ficaria se fosse usado o POP3 ao invés do IMAP, funciona tb?

Obrigado.

Helcio disse...

Tentei com uma conta do Gmail, mesmo ativando o IMAP ele não funcionou, chega a conectar mas não lê a caixa e trava.

Eduardo Bregaida disse...

Helcio, tudo bem? eu não cheguei a fazer conectando ao POP3, no Gmail você não é o primeiro que reclama desse problema, esse projeto eu fiz conectando num Lotus Notes, mas você pegou o código aqui do blog ou do GitHub? Lá está mais atualizado.
No post do GUJ o alexandrenilton fez acessando o GMail utilizando a solução que havia postado, dê uma olhadinha o link está no começo do tópico.

Helcio disse...

Bom dia Eduardo obrigado pela resposta. Eu realmenet preciso que seja IMAP como vc fez. Minha conta no gmail está ativada essa opção. Olhando o post do alexandrenilton fiz algumas modificações mas não obtive exito.

eu alterei as contantes dessa forma:

public static final String PASTA_XML = "C:/pasta/aplicacao/aqui_ser‡_salvo/";
public static final String IMAP = "imap";
public static final String HOST = "imap.gmail.com";
public static final int PORTA = 993;
public static final String ARQUIVO_MSG = "Inbox";
public static final String LOGIN = "user@gmail.com";
public static final String SENHA = "senha";
public static final String PASTA_PRINCIPAL = "Inbox";
public static final String PASTA_BACKUP = "backup";


Só que no monitor e no log fica aguardando :

INFO 15.08.2012 11:34:20 main br.com.email.dados.main.PrincipalNfe - #################### INICIANDO O PROCESSAMENTO DOS E-MAILS ##################
INFO 15.08.2012 11:34:20 main br.com.email.dados.email.ManipularEmail - Conectando ao servidor de e-mail

Eduardo Bregaida disse...

Pelo que entendi ele não está conectando, faz o seguinte, envia o fonte que vou plugar com meu gmail e debugar pra ver o que está acontecendo. eduardo.bregaida@gmail.com

[]ss

Helcio disse...

Opa, obrigado Eduardo. Então eu só mudei as constantes conforme a mensagem acima. O restante está igual ao seu projeto eu baixei o zip do GitHub. Se conseguir alguma luz vai me ajudar muito.

Eduardo Bregaida disse...

Ok, a noite vou olhar, qualquer novidade te aviso por aqui.

Eduardo Bregaida disse...

Helcio, demorou mas consegui, tenta assim:

//Gmail
public static final String PASTA_XML = "c:/SuaPasta";
public static final String IMAP = "imaps";
public static final String HOST = "imap.gmail.com";
public static final int PORTA = 993;
public static final String ARQUIVO_MSG = "Inbox";
public static final String LOGIN = "seuEmail@gmail.com";
public static final String SENHA = "suaSenha";
public static final String PASTA_PRINCIPAL = "Inbox";
public static final String PASTA_BACKUP = "backup";

Helcio disse...

Boa Eduardo aqui funcionou também, basicamente era o public static final String IMAP = "imaps"; eu havia colocado imap sem o 's'. Obrigado pela ajuda e parabéns pela iniciativa.

Eduardo Bregaida disse...

Eu tbm não sabia, testei outras formas e li alguns artigos para descobrir isso, mas o bom é que funcionou, boa sorte aí no seu projeto.

[]s

Ernesto Niklaus disse...

Eduardo,

Estou usando um código semelhante e estou tento problemas para receber arquivos de NFE em anexo enviados pelo SAP. Aparementente o SAP GRC não envia multipart corretamente, ou seja, não tem aquela separação de boundary na mensagem, com isso o javax.mail não identifica como anexo. Já teve problema semelhante a este? Sabe como fazer o programa entender que o que vem depois dos cabeçalhos é um anexo?

divonei disse...

parabéns Eduardo modulo show de bola quero ver se eu consigo ajustar um layout para o modulo e fazer com que ele envie via ftp os arquivos da nota

Eduardo Bregaida disse...

Ernesto, infelizmente nunca tive esse problema, até mesmo porque na empresa que usava não havia SAP.

Divonei, blz, coloquei aqui depois as duvidas e resoluções que você achou.


[]sss

TecnoDigital disse...

Boa noite Eduardo nao consigo rodar fala pra consultar este site
See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

André disse...

Galera tentei fazer a conexão mas obtive o seguinte erro:

#################### INICIANDO O PROCESSAMENTO DOS E-MAILS ##################
#################### FIM DO PROCESSAMENTO DOS E-MAILS ##################
javax.mail.MessagingException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target;
nested exception is:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:571)
at javax.mail.Service.connect(Service.java:288)
at javax.mail.Service.connect(Service.java:169)
at javax.mail.Service.connect(Service.java:118)
at br.com.email.dados.email.ManipularEmail.conectar(ManipularEmail.java:47)
at br.com.email.dados.acao.ReadEmails.(ReadEmails.java:51)
at br.com.email.dados.main.PrincipalNfe.main(PrincipalNfe.java:24)

Entendi o erro mas não entendi o pq? Como gero esse certificado?

Eduardo Bregaida disse...

Qual email vc está tentando conectar? Gmail?

\\ JP disse...

Bom dia Eduardo,
Primeiramente parabéns pelo post, muito bacana.
Estou iniciando um projeto, também para gerenciamento de arquivos XML (no meu caso, arquivos de nf-e) e estava com dúvida sobre esta pasta auxiliar, esta pasta fica no webmail?

Obrigado mais uma vez por compartilhar.

Att.
João Paulo

Eduardo Bregaida disse...

Mal a demora JP, pasta de Backup sim, dentro do email, pasta XML é a que será salva no seu PC.
Essa aplicação é exatamente para a NFE =)

sanches disse...

Olá, excelente post, me ajudou muito... mas só consegui conectar no gmail, tentei yahoo e ig e não tive sucesso. Sabe me dizer o que muda?

Carlos Arbigaus disse...
Este comentário foi removido pelo autor.
Carlos Arbigaus disse...

Olá Eduardo.
Dias atrás pesquisei muito na web, pois precisava bem isto o que você fez. Ficou excepcional. Porém, faz pouco tempo que estou aprendendo Java. Seu sistema rodou 100%. Mas gostaria de saber se posso imprimir os resultados num JTextArea por exemplo. Pois ele imprime no depurador. No caso eu queria que fosse mostrando num JTextArea. Saberia me dar algum exemplo?
Aquele abraço e parabéns pela postagem. Excelente

Eduardo Bregaida disse...

Sanches, eu não tentei por esses outros tipos de email mas com certeza dá, basta configurar o POP3 ou IMAP e outros parâmetros dos mesmos.

Carlos vamos lá:
Você quer imprimir os System.out? Dá sim, você pode criar algo como:
String imprime = "Movendo e-mail para pasta backup!";
JTextArea meuTextArea = new JTextArea();
meuTextArea.setText(imprime);

http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JTextArea.html


Rafael Santos disse...

Olá Eduardo.

Seu post me ajudou muito, só não consegui fazer um filtro pra pegar os e-mails que vem com determinado assunto. (Estou usando para fazer uma integração da página de contatos do cliente para o CRM). Alguma idéia de como posso fazer isso?

Eduardo Bregaida disse...

opa tentou com o getSubject criar um método para validar o assunto que tá chegando?
geronimo.apache.org/maven/specs/geronimo-javamail_1.4_spec/1.6/apidocs/javax/mail/Message.html

Ministerio Mergulhar mais Fundo disse...

Boa Tarde,

Estou tentando salvar arquivos .txt é aparece o seguinte erro:
java.io.FileNotFoundException: D:\Teste Sistema\=?UTF-8?B?U0lQX1NQT0xPRy50eHQ=?= (The filename, directory name, or volume label syntax is incorrect)

Alguém pode me ajudar?

Att,

Eduardo Bregaida disse...

Então colocar caminho posicional é complicado, pois muda de ambiente para ambiente, ele pode não pegar no contexto, tente mudar para um caminho relativo:

http://respostas.guj.com.br/100-como-pegar-o-caminho-relativo-de-um-arquivo-e-transformar-em-absoluto


[]ssss

Juliana G. disse...
Este comentário foi removido pelo autor.
Eduardo Bregaida disse...

Oi Juliana, tudo bem?
Então eu criei a classe primeiro pra não espalhar um monte de constante na classe de ReadEmails, apenas por estética e limpeza, toda aquela parte de configuração fica chumbada num lugar separado para qualquer constante do sistema, em termos práticos não muda nada, apenas diminui um pouco a sujeira na classe mais importante :) só estética mesmo.

Juliana G. disse...

Oi Eduardo.. Primeiramente, obrigada pelo código! Ajudou Muito! ^.^

Os arquivos xml não estão sendo enviados para a pasta.. :c Você tem alguma ideia do que pode estar acontecendo?

Juliana G. disse...

Muito obrigada pela atenção e pela resposta anterior, Eduardo!

Eduardo Bregaida disse...

Precisando só chamar

Juliana G. disse...

Então..só mais uma coisa.. o código funcionou mas os arquivos não estão aparecendo na pasta T.T vc tem alguma ideia do por quê? A validação tá funcionando..mas parece que a parte de salvar não.. Eu mandei salvar em uma pasta dentro do projeto

Eduardo Bregaida disse...

Está gerando alguma Exception?

Eduardo Bregaida disse...

Vc tem algum contato Juliana?