quinta-feira, 9 de outubro de 2014

Java + FTP - É possível ?

Construir um projeto Java que precise utilizar de um servidor FTP, é possível ? Terei de usar ferramentas externas para ter acesso aos arquivos ? O que é FTP ?

Todas essas perguntas serão respondidas hoje. Vamos começar pelo começo. FTP é a sigla para File Transfer Protocol o qual, é um protocolo da camada de Aplicação do modelo OSI. Como o próprio nome já diz, esse protocolo é usado para transferência de arquivos. Não entrarei a fundo na explicação do FTP. O que você precisa saber para este tópico está escrito neste parágrafo.

Construir um projeto Java que precise utilizar de um servidor FTP, é possível ? Sim, é possível e não somente, é possível como, é fácil também.

Terei de usar ferramentas externas para ter acesso aos arquivos ? Não. Salvo somente os casos em que seja obrigatório usar alguma ferramenta externa ou, seja de vontade do próprio desenvolvedor.

Agora que já sabemos que é possível trabalhar com Java + FTP, vamos ao exemplo.

Primeira etapa - Montar o ambiente

Montar um ambiente Java + FTP é fácil. Precisamos de:

  • Eclipse Luna (4.4.0)
  • Maven (se quiser)
  • Jar Commons Net 3.3 (caso não use Maven)
Caso você não queira usar o Maven, sem problemas. Baixe o JAR do Commons Net, clicando aqui. No nosso exemplo usaremos o Maven. Crie um Maven Project com o archtype quickstart. Caso você possua alguma dúvida com a criação do projeto, nosso amigo Mkyong realizou uma ótima contribuição, através do link.Com o projeto criado, insira a seguinte dependência:

<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>

Depois de ter inserido a dependência, já podemos começar a programar.

Segunda etapa - Programação

Para habilitar o FTP na sua máquina é necessário realizar algumas configurações no Windows, clique aqui para mais informações. Com seu servidor FTP habilitado e criado, vamos para o Java.

Crie uma classe com o nome de App. Depois de ter criado a classe, vamos criar duas constantes (LOGIN e PASSWORD).

private static final String LOGIN = "meu_usuario";
private static final String PASSWORD = "minha_senha";

Crie um método main e crie um método chamado gerarConexao(). Este método será responsável por gerar a conexão FTP e retornar o objeto que representa o mesmo.

private FTPClient gerarConexao() throws SocketException, IOException{
    FTPClient ftpClient = new FTPClient();
    ftpClient.connect("127.0.0.1"); //Conecta ao Servidor FTP
    ftpClient.login(LOGIN, PASSWORD); //efetua o login no Servidor
    //ftpClient.changeWorkingDirectory("minha_pasta"); //Muda o diretório de trabalho do servidor
    return ftpClient;
}

O código acima é bem tranquilo. Creio que não necessito fazer mais explicações.

Com o nosso método de realizar conexões feito, podemos trabalhar diretamente com os arquivos. Vamos agora, listar todos os arquivos do servidor. Construa o método lerArquivos(FTPClient ftpClient). Este método recebe uma instância de FTPClient o qual, representa nossa conexão.

private void lerArquivos(FTPClient ftpClient) throws IOException{
    FTPFile[] arqs = ftpClient.listFiles(); //lista os arquivos
        System.out.println ("Listando arquivos: \n");
        for (FTPFile f : arqs){
        System.out.println(f.getName() + " - " + f.getType()); //imprimo o nome do arquivo e seu tipo
        }
}

O método acima também é bem tranquilo. Abaixo, listo alguns métodos de listagem de arquivos.
  • listFiles() - Retorna uma lista de Objetos do tipo FTPFile. Para mais informações sobre a classe, clique aqui.
  • listNames(String caminho) – Mesmo que o anterior, mas passando como parâmetro o caminho do diretório a ser listado.
  • listNames() - Retorna um array de Strings contendo o nome dos arquivos no diretório de trabalho atual.
Agora vamos escrever um método para por arquivos no nosso servidor FTP. Escreva o método escreverArquivo(FTPClient ftpClient). Este método recebe uma instância de FTPClient 
qual, representa nossa conexão.

private void escreverArquivo(FTPClient ftpClient) throws IOException{    
    FileInputStream novoArq = new FileInputStream("C:\\minha_pasta\\arquivo_origem.txt");
    if(ftpClient.storeFile("nome_novo_arquivo.txt", novoArq)){
    System.out.println("Arquivo armazenado com sucesso!");
    } else {
    System.out.println("Erro ao armazenar o arquivo.");
    }
}

O método acima cria um novo arquivo no FTP com o nome especificado no primeiro parâmetro do método storeFile. Este método retorna um boolean que, informa se a criação foi feita com sucesso ou não. O parâmetro novoArq representa o arquivo de origem da gravação.

Nosso último método, terá a tarefa de realizar o download de um arquivo que está no FTP. Construa o método recuperarArquivo(FTPClient ftpClient). Este método recebe uma instância de FTPClient o qual, representa nossa conexão.

private void recuperarArquivo(FTPClient ftpClient) throws IOException{
    FileOutputStream fos = new FileOutputStream("C:\\minha_pasta\\arquivo_download.txt");
    if (ftpClient.retrieveFile("arquivo_fonte.txt", fos ))
    System.out.println("Download efetuado com sucesso!");
    else
            System.out.println ("Erro ao efetuar download do arquivo.");
}

Acima, é possível notar que o método retrieveFile recebe dois parâmetros. O primeiro parâmetro representa o nome do arquivo que será feito o download e o segundo representa o destino do arquivo. Este método retorna um boolean que, indica se houve êxito ou não.

Precisamos de um método agora para realizar desconexão do nosso programa com o servidor FTP. Escreva o método logout(FTPClient ftpClient). Este método recebe uma instância de FTPClient o qual, representa nossa conexão.

private void logout(FTPClient ftpClient) throws IOException{
    ftpClient.logout();
    ftpClient.disconnect();
}

Este método dispensa explicações. Por último, temos o método main que chama todos os outros métodos.

public static void main( String[] args ) throws SocketException, IOException 
    {
    App app = new App();
    FTPClient ftpClient = app.gerarConexao();
    app.lerArquivos(ftpClient);
    app.escreverArquivo(ftpClient);
    app.recuperarArquivo(ftpClient);
    app.logout(ftpClient);
    }

Caso você queira baixar o projeto, clique aqui.

É possível notar, que trabalhar com Java e FTP é simples. Procurei por uma abordagem 100% Java mas, não achei. Quem souber, deixe nos comentários sua contribuição. A API Commons Net faz todo o trabalho pesado e para o desenvolvedor só fica a tarefa de processar os arquivos.

Referências:
http://www.devmedia.com.br/desenvolvendo-um-cliente-ftp/3547

17 comentários:

Matheus Bigai Ferreira disse...

Olá, bom dia!
Estou com um problema na hopra de tentar baixar um arquivo do FTP usando o Java.
"private void recuperarArquivo(FTPClient ftpClient) throws IOException {
File file = new File("F:\\NetBeansProjects");
FileOutputStream fos = new FileOutputStream(file.getName());
if (ftpClient.retrieveFile("conexao.txt", fos)) {
System.out.println("Download efetuado com sucesso!");
} else {
System.out.println("Erro ao efetuar download do arquivo.");
}
}"
Essa é minha linha de código que usei para fazer o download, mas quando a execução chega na linha do if, ela trava, se você poder me ajudar agradeceria muito.
E a proposito, ótimo post.

Preciso Estudar Sempre disse...

Olha meu amigo, eu daria um chute inicial que isto pode acontecer devido à timeout. Mas me diga uma coisa, como esta instância de FTPClient está sendo criada ? Preciso entender melhor o seu problema.

Na postagem eu ponho instruções detalhadas de como adquirir uma conexão via FTP. Contudo, lembre-se que você precisa de um servidor FTP, sem isso não rola.

Espero ter ajudado. Qualquer coisa entre em contato e vamos vendo juntos.

Matheus Bigai Ferreira disse...
Este comentário foi removido pelo autor.
Matheus Bigai Ferreira disse...

Sim, tenho um servidor FTP, até conecta tudo certo, vou mandar o código da classe inteira:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package metodosComplementares;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

public class FTPConnect {

private static final String LOGIN = "";
private static final String PASSWORD = "";

private FTPClient gerarConexao() throws SocketException, IOException {
FTPClient ftpClient = new FTPClient();
ftpClient.setDefaultPort(2121);
ftpClient.connect("192.168.88.12"); //Conecta ao Servidor FTP
ftpClient.login(LOGIN, PASSWORD); //efetua o login no Servidor
//ftpClient.changeWorkingDirectory("minha_pasta"); //Muda o diretório de trabalho do servidor
return ftpClient;
}

private void lerArquivos(FTPClient ftpClient) throws IOException {
FTPFile[] arqs = ftpClient.listFiles(); //lista os arquivos
System.out.println("Listando arquivos: \n");
for (FTPFile f : arqs) {
System.out.println(f.getName() + " - " + f.getType()); //imprimo o nome do arquivo e seu tipo
}
}

private void escreverArquivo(FTPClient ftpClient) throws IOException {
FileInputStream novoArq = new FileInputStream("C:\\minha_pasta\\arquivo_origem.txt");
if (ftpClient.storeFile("nome_novo_arquivo.txt", novoArq)) {
System.out.println("Arquivo armazenado com sucesso!");
} else {
System.out.println("Erro ao armazenar o arquivo.");
}
}

private void recuperarArquivo(FTPClient ftpClient) throws IOException {
File file = new File("F:\\NetBeansProjects");
FileOutputStream fos = new FileOutputStream(file.getName());
if (ftpClient.retrieveFile("conexao.txt", fos)) {
System.out.println("Download efetuado com sucesso!");
} else {
System.out.println("Erro ao efetuar download do arquivo.");
}
}

private void logout(FTPClient ftpClient) throws IOException {
ftpClient.logout();
ftpClient.disconnect();
}

public static void main(String[] args) throws SocketException, IOException {
FTPConnect app = new FTPConnect();
FTPClient ftpClient = app.gerarConexao();

app.recuperarArquivo(ftpClient);
app.logout(ftpClient);
}
}
A parte do login e senha não tá em branco tá... KKKK
E se for timeout como posso resolver isso?

Preciso Estudar Sempre disse...

Desculpa a demora cara ... ficou difícil responder por causa do carnaval.

Notei que seu método recuperarArquivo está diferente do método que descrevi na postagem. Dê uma olhada em como você cria FileOutputStream.

Matheus Bigai Ferreira disse...

Sem problemas.
Então, eu acresentei a linha do File, por que está dando esse erro:
Exception in thread "main" java.io.FileNotFoundException: F:\NetBeansProjects (Acesso negado)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.(FileOutputStream.java:213)
at java.io.FileOutputStream.(FileOutputStream.java:101)
at metodosComplementares.FTPConnect.recuperarArquivo(FTPConnect.java:51)
at metodosComplementares.FTPConnect.main(FTPConnect.java:67)
Picked up _JAVA_OPTIONS: -Xmx512m
C:\Users\Matheus\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
FALHA NA CONSTRUÇÃO (tempo total: 0 segundos)
Eu olhei em vários lugares para tentar arrumar isso, e acrecentar o File foi a solução que achei.

Preciso Estudar Sempre disse...

A resposta para isso é clara. Ou você não possui essa unidade de sistema (F), ou acessá-la é proibido. Tente criar a mesma pasta no seu C, mas ao invés de acessar a pasta diretamente, acesso o arquivo lá dentro.

Ex.:
Não faça isso
File f = new File("C:\\pasta");

Tente assim
File f = new File("C:\\pasta\\arquivo.txt");

Matheus Bigai Ferreira disse...

Você é um máximo... Deu certinho agora.
Desculpa se minhas perguntas foram meias banais, mas agradeço muito pela ajuda.

Preciso Estudar Sempre disse...

Fico feliz que você tenha tido êxito em sua empreitada. Agradeço muito o elogio, mas acredito que não sou merecedor de tal pois assim como você sou um apenas estudante vislumbrado com a imensidão do universo e aqui neste blog tento contribuir para o mundo.

Não existem dúvidas banais. Todas as dúvidas são importantes. Sinta-se sempre bem-vindo para tirar aqui suas dúvidas.

Matheus Bigai Ferreira disse...

Desculpa, deixa eu te perguntar outra coisa.
Ele está fazendo o Download normalmente, e salvando onde eu preciso que ele salve, mas a mensagem de que o Download foi terminado não aparece, e o Sistema fica executando a função de recuperação sem parar.
O que pode ser isso?

Mesmo assim você está de parabéns.

Preciso Estudar Sempre disse...

Olha cara, fica difícil te dizer o que está acontecendo sem executar o seu programa. Preciso que você me envie mais dados da sua execução, como por exemplo saída do console, exceptions, prints, etc.

Matheus Bigai Ferreira disse...

Desculpa, é meio complicado postar todo o Código aqui.
Mas deixa eu tentar explicar melhor.
Estou usando esse método para fazer download do arquivo no FTP:
public void recuperarArquivoExecutavel(FTPClient ftpClient) throws IOException{
File file = new File(".\\AgendaTarefas.jar");
FileOutputStream fos = new FileOutputStream(file);
if (ftpClient.retrieveFile("AgendaTarefas.jar", fos)){
// Quando chega aqui, ele faz download normalmente, mas a mensagem não aparece
System.out.println("Download efetuado com sucesso!");
ftpClient.logout();
ftpClient.disconnect();
}else{
System.out.println ("Erro ao efetuar download do arquivo.");
ftpClient.logout();
ftpClient.disconnect();
}
}
Até debuguei o código, e ele funciona normalmente, até aparece a mensagem, então não entendi o que pode estar acontecendo.
Esse mé o metodo que estou usando para chamar o download do FTP:
FTPConnect app = new FTPConnect();
FTPClient ftpClient;
try {
ftpClient = app.gerarConexao();
app.recuperarArquivoExecutavel(ftpClient);
} catch (IOException ex) {
Logger.getLogger(TelaAtualizacao.class.getName()).log(Level.SEVERE, null, ex);
}

Preciso Estudar Sempre disse...

O que acontece quando ele acaba de executar o retrieveFile ? Ele trava ? Se não, o que acontece quando ele passa em cima do sysout ?

Matheus Bigai Ferreira disse...

Normalmente ele não passa por ele, ele fica preso no if (ftpClient.retrieveFile("AgendaTarefas.jar", fos)){ e não segue para o sysout.
Só consegui fazewr isso no debug, que com o F5 ele foi.

Preciso Estudar Sempre disse...

Se ele tá dando esse problema é porque existem problemas na recuperação do arquivo. Tente por o caminho completo do arquivo (absolute path) nessa sua instância de File.

Matheus Bigai Ferreira disse...

Acredito que o problema esteja no meu Servidor FTP, ele não chega a finalizar a transferência do arquivo, mesmo que ele carregue todo o arquivo, ele não finaliza e dá timeout.

Preciso Estudar Sempre disse...

Parabéns por ter encontrado a raiz do problema. Pense agora em talvez melhorar o seu tratamento de erro para timeouts pois um usuário pode ficar confuso caso o seu programa não informe nada.

Dê uma olhada no seu servidor FTP nas permissões que ele fornece para este arquivo. Pode ser que o seu arquivo não possua permissão de download.

Aproveito também para realizar um jabá próprio e pedir, se não for muito incômodo, que você siga/inscreva no blog para ficar por dentro das novas postagens.