Android - Download e Upload de arquivos via FTP


Olá Amigos do The Club, neste artigo abordarei um assunto interessante para quem deseja realizar a troca de arquivos do Android para outro sistema utilizando o protocolo FTP. Para quem não conhece, qual seria o significado da sigla FTP? FTP significa “File Transfer Protocol”  que traduzindo para o português seria: Protocolo de Transferência de Arquivos,  é uma forma bastante rápida e versátil de transferir arquivos, sendo uma das mais usadas na internet. Nada mais é do que um sistema de transferência de arquivos cliente/servidor. Isto quer dizer, que utilizando este protocolo você poderá enviar e receber arquivos do seu computador (cliente) para o host (servidor) onde fica hospedado o seu site, blog, entre outros. O Sistema Android, junto com outras bibliotecas nos fornece todo o aparato necessário para trabalharmos com este tipo de transferência.

Baixando a Biblioteca


Para conciliarmos esta integração do Android junto com tarefas de transferência de arquivos via FTP deveremos baixar uma biblioteca do Apache no link a seguir. Ver Imagem 01. http://commons.apache.org/net/download_net.cgi


Imagem 01. Endereço do Site.


Utilizaremos a versão 3.1 que até a data de hoje é a mais estável e atual. O nome do arquivo baixado deverá ser semelhante a “commons-net-3.1-bin.zip”. Descompacte-o em uma pasta e observe três arquivos importantes que será importado mais adiante: - “commons-net-3.1.jar”
- “commons-net-3.1-sources.jar”
- “commons-net-examples-3.1.jar”

Importando a Biblioteca


Para isto abra seu Eclipse e clique em “File/New/Android Project” e crie um projeto em Android, recomendo a criação na versão 2.2 ou 2.3 utilizando o nome que desejar. No meu caso criei como “AndroidFTP”. Clique com o botão direito sobre o mesmo e escolha Propriedades, Ver Figura 02.


Imagem 02. Propriedades do Projeto.


“Navegue até o item “Java Build Path” e na aba “Libraries” adicione os arquivos “.jar” citados anteriormente. Recomendo criar uma pasta no projeto para uma melhor organização, no meu caso foi criada como “bibliotecas”. Ver Imagem 03.


Imagem 03. Adicionando as bibliotecas necessárias.


Na estrutura do projeto teremos uma “Referenced Libraries”, que nada mais é do que as bibliotecas adicionadas anteriormente. Ver Imagem 04.


Imagem 04. Biblioteca importada com sucesso.




Desenvolvendo um exemplo prático


Dividiremos o exemplo em duas partes, na primeira criaremos uma classe com os métodos responsáveis por fazer a integração com o FTP e a segunda uma Atividade para utilizar estes métodos.

Criando a Classe FTP


Para isto clique com o botão direito e escolha “Add/New/Class” e defina o nome como “classe_FTP”. Importe os seguintes pacotes:


import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import android.os.Environment;
import android.util.Log;



Defina duas variáveis globais:


FTPClient mFtp; 
String TAG = "classeFTP";



Segue em seguida a classe completa e com os métodos devidamente comentados para melhores detalhes.


public class classe_FTP
{
    FTPClient mFtp; 
    private String TAG = "classeFTP";
   

public
FTPFile[] Dir(String Diretorio)
{
        try
        {
 FTPFile[] ftpFiles = mFtp.listFiles(Diretorio);
          return ftpFiles;
        }
        catch(Exception e)
        {
Log.e(TAG, "Erro: não foi possível  listar os   arquivos e pastas do diretório " + Diretorio + ". " + e.getMessage());
        }
       
return null;
}   



O método “Dir” é responsável por listar os arquivos e diretórios de um determinado caminho. Tem como parâmetro de entrada uma String “Diretorio”, onde inserimos o diretório para o qual irá ser listado dos arquivos e como saída uma variável do tipo FTPFile[] que nada mais é que uma variável idêntica ao tipo File[], responsável por armazenar o nome dos arquivos/diretórios.


public boolean MudarDiretorio(String Diretorio)
{
     try
     {
         mFtp.changeWorkingDirectory(Diretorio);
     }
     catch(Exception e)
     {
Log.e(TAG, "Erro: não foi possível mudar o diretório para " + Diretorio);
     }
       return false;
}  



Este método é utilizado quando desejamos utilizar o mesmo diretório corrente. O método “changeWorkingDirectory()” que realize esta tarefa.


public boolean Desconectar()
{
        try
        {
            mFtp.disconnect();
            mFtp = null;
            return true;
        }
        catch (Exception e)
        {
            Log.e(TAG, "Erro: ao desconectar. " + e.getMessage());
        }
       
   return false;



O método “Desconectar()”, como o próprio nome diz, faz o trabalho de desconectar ou encerrar a conexão do servidor FTP usando o método “disconnect()”.


public boolean Conectar(String Host, String Usuario, String Senha, int Porta)
{
  try
  {
     mFtp = new FTPClient();
  
     mFtp.connect(Host, Porta);
    
     if (FTPReply.isPositiveCompletion(mFtp.getReplyCode)))
     {
        boolean status = mFtp.login(Usuario, Senha);
 
        mFtp.setFileType(FTP.BINARY_FILE_TYPE);
        mFtp.enterLocalPassiveMode();
                
        return status;
     }
  }
  catch(Exception e)
  {
     Log.e(TAG, "Erro: não foi possível conectar" + Host);
  }
     return false;



Já o método “Conectar()” inicia uma sessão com o servidor FTP, possuindo como parâmetro de entrada o servidor FTP, o usuário, a senha e a porta utilizada. Esta função nos retorna uma variável do tipo booleana para nos certificarmos que a conexão foi efetuada com sucesso.


public boolean Download(String DiretorioOrigem, String ArqOrigem, String ArqDestino) 

     boolean status = false
       
try

            MudarDiretorio(DiretorioOrigem);
            
FileOutputStream desFileStream = new FileOutputStream(ArqDestino);; 
            
  mFtp.setFileType(FTP.BINARY_FILE_TYPE); 
       mFtp.enterLocalActiveMode() 
            mFtp.enterLocalPassiveMode(); 
           
status =  mFtp.retrieveFile(ArqOrigem, desFileStream); 
  desFileStream.close();
            Desconectar();
    
            return status; 
        }
        catch (Exception e)
        { 
Log.e(TAG, "Erro: Falha ao efetuar download. " + e.getMessage()); 
        } 
       
return status; 
}



O método Download recebe os seguintes parâmetros: O diretório de origem, o arquivo de origem e o de destino. É criada uma variável do tipo “Outputstream” para o arquivo ser passado como parâmetro, logo em seguida definimos o tipo de arquivo como “BINARY_FILE_TYPE”. O método mais importante é o “RetrieveFile” sendo responsável por baixar o arquivo. Após estas etapas executamos o método Desconectar(). Temos como retorno um booleano que indica se ocorreu tudo corretamente.


public boolean Upload(String diretorio, String nomeArquivo)
{
   boolean status = false
   try
   {
FileInputStream arqEnviar = new  FileInputStream(Environment.getExternalStorageDirectory() + diretorio);
             mFtp.setFileTransferMode(FTPClient.STREAM_TRANSFER_MODE);
             mFtp.setFileType(FTPClient.STREAM_TRANSFER_MODE);
     mFtp.storeFile(nomeArquivo, arqEnviar);
     Desconectar();
     return status;
   }
   catch (Exception e)
   {
Log.e(TAG, "Erro: Falha ao efetuar Upload. " +  e.getMessage());
   }
   return status;
}



O método Upload faz o inverso, ou seja, estamos salvando os dados do Android para o Servidor FTP, tendo como parâmetro de entrada o diretório e o nome do arquivo e de saída uma variável do tipo booleana. Foi criado uma variável do tipo “FileInputStream” para armazenar o arquivo a enviar e logo em seguida usamos o “setFileType” e “SetFileTransferMode” para indicar o tipo de transferência de arquivos, que no caso usaremos como “Stream”. Finalmente transferimos o arquivo usando o método “storeFile” seguindo o “Desconectar” . Todos os métodos implementados anteriormente foi utilizado o bloco de comandos “Try..Catch” e “Log.e” para nos dar mais detalhes caso exista alguma falha no decorrer de sua execução.

Montando o “Lay-Out”


O exemplo utilizará os seguintes componentes: - 2 Spinners - 4 TextViews - 2 Buttons A Imagem 05 nos dá uma idéia do “Lay-out”.


Imagem 05. Sugestão de “Lay-Out”.


O XML correspondente ficou da seguinte maneira:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <TextView android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:id="@+id/textView4"
    android:text="Download de Arquivos">
    </TextView>
    <TextView android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:id="@+id/textView2"
    android:text="Lista de Arquivos do FTP:">
    </TextView>
    <Spinner android:id="@+id/spinner1"
    android:layout_width="310dp"
    android:layout_height="50dp"
    </Spinner>
    <Button android:text="Efetuar Download"
    android:id="@+id/Button01"
    android:layout_width="140dp"
    android:layout_height="wrap_content"
    android:onClick="Download_click"
    android:layout_gravity="center_horizontal">
    </Button>
    <TextView android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:id="@+id/textView5"
    android:text="Upload de Arquivos">
    </TextView>
    <TextView android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:id="@+id/textView1"
    android:text="Lista de Arquivos do Celular:">
    </TextView>
    <Spinner android:id="@+id/spinner2"
    android:layout_width="310dp"
    android:layout_height="50dp">
    </Spinner>
    <Button android:text="Efetuar Upload"
    android:id="@+id/button1"
    android:layout_width="140dp"
    android:layout_height="wrap_content"
    android:onClick="Upload_click"
    android:layout_gravity="center_horizontal">
    </Button>
 </LinearLayout>





Codificando a Atividade Principal


A atividade será composta de algumas funções para Downloads e Uploads. Usaremos também “Threads” para indicar o processamento das tarefas. Comentarei o código a seguir para melhor entendimento. Utilizaremos as seguintes bibliotecas:


package pct.AndroidFTP; import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import java.io.File;
import java.util.ArrayList;
import org.apache.commons.net.ftp.FTPFile;
import android.os.Environment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
 
public class AndroidFTPActivity extends Activity
{
  
   Spinner SpnListarArquivosFTP;
   ArrayList<String> ArquivosFTP = new ArrayList<String>();
   Spinner SpnListarArquivosCelular;
   ArrayList<String> ArquivosCelular = new   ArrayList<String>();
   ProgressDialog dialog;



Estas são as variáveis globais que utilizaremos ao decorrer do desenvolvimento, o Spinner e o ArrayList serão responsáveis por armazenar os nomes dos arquivos localizados tanto no celular quanto no servidor FTP.  


  @Override
  public void onCreate(Bundle savedInstanceState)
  {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
              
SpnListarArquivosFTP = (Spinner)    findViewById(R.id.spinner1);
SpnListarArquivosCelular = (Spinner) findViewById(R.id.spinner2);
     ListarArquivosdoFTP();
     ListarArquivosdoCelular();
  }



No evento OnCreate é inicializado os “listeners” SpnListarArquivosFTP e SpnListarArquivosCelular. Também são invocadas os métodos ListarArquivosFTP() e ListarArquivosdoCelular() que são respectivamente responsáveis por listar os arquivos do servidor e do celular.


public void ListarArquivosdoFTP()
  {
     classe_FTP ClienteFTP = new classe_FTP();
ClienteFTP.Conectar("ftp.teste.com.br", "the club", "123mudar", 21);
     FTPFile[] arquivos = ClienteFTP.Dir("/thiago"); 

     if(arquivos != null)
{
        int length = arquivos.length;
        for(int i = 0; i < length; ++i)
        {
             FTPFile f = arquivos[i];
             if(f.isFile())
             {
              ArquivosFTP.add(f.getName());
             }
        }
ArrayAdapter<String> arrayAdapter = new  ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line, ArquivosFTP);
         SpnListarArquivosFTP.setAdapter(arrayAdapter);
     }  
     
  }

Método que trás os arquivos que estão localizados no servidor FTP. Inicialmente instanciamos a “classe_FTP” e usamos o método Conectar(), passando os seguintes parâmetros: Nome do Servidor FTP: “ ftp.teste.com.br” Usuário: “the club” Senha: “123mudar” Porta: 21 Estes mesmos dados serão utilizados posteriormente. O método “ClienteFTP.Dir()” recebe como parâmetro o nome da pasta que iremos utilizar para listar os arquivos, no nosso caso utilizei uma chamada “/thiago” para fins de testes. Por final faremos um “for” nestes arquivos para adicionarmos no Spinner (SpnListarArquivosFTP).


public void ListarArquivosdoCelular()
  {
    File diretorio = new File("/mnt/sdcard");
     File[] arquivos = diretorio.listFiles();

    if(arquivos != null)
    {
             int length = arquivos.length;
        
             for(int i = 0; i < length; ++i)
             {
                 File f = arquivos[i];
                 if(f.isFile())
                 {
                   ArquivosCelular.add(f.getName());
                 }
             }
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line, ArquivosCelular);
         SpnListarArquivosCelular.setAdapter(arrayAdapter);
    } 
  }

Este método é bem parecido com o descrito anteriormente, só que estamos listando os arquivos que estão localizados dentro do celular ou do tablet. No caso estamos passando como parâmetro o caminho “/mnt/sdcard” que é o caminho padrão de armazenamento externo.


public void Efetuar_Download()
  {
        String lstrArq = "";
       
        try
        {
            classe_FTP ClienteFTP = new classe_FTP();
            
      
lstrArq = "/"+SpnListarArquivosFTP.getSelectedItem().toString();
File lArquivo = new File(Environment.getExternalStorageDirectory(), lstrArq);
ClienteFTP.Conectar("ftp.teste.com.br", "the club", "teste123", 21);
            
ClienteFTP.Download("/thiago", SpnListarArquivosFTP.getSelectedItem().toString(), lArquivo.toString());
                
        }
        catch (Exception e)
        {
            trace("Erro : " + e.getMessage());
        }
     
  }



O método “Efetuar_Download()” utiliza a classe_FTP. Selecionaremos o item desejado no “SpnListarArquivosFTP” (Os arquivos que estão no servidor FTP) e logo em seguida conectando no FTP chamando o método “Download()”. Os parâmetros são respectivamente: Nome da Pasta, Nome do Arquivo a baixar e Nome do Arquivo a ser salvo junto com o caminho.



  public void Download_click(View v)
  { 
dialog = ProgressDialog.show(AndroidFTPActivity.this,"The Club","Sincronizando Dados . . .", false, true);
    dialog.setCancelable(false);
     
    new Thread()
    {
       public void run()
       {
              try
              {
                   Efetuar_Download();
                   dialog.dismiss();
              }
             catch (Exception e)
             {
                trace("Erro : " + e.getMessage());
             }
             
       }
    }.start();
  }



Este é o evento do botão “Download()”, onde invocaremos a função criada no momento anterior. Faremos o uso de Threads para execução de múltiplas tarefas e para dar uma melhor impressão ao usuário. Usaremos também a classe “ProgressDialog.Show()” que tem como objetivo mostrar uma mensagem informativa até o término do processamento desta operação.



  public void Efetuar_Upload()
  {
    try
    {   
String lstrArq = "/"+SpnListarArquivosCelular.getSelectedItem().toString();
         classe_FTP ClienteFTP = new classe_FTP();
                 
ClienteFTP.Conectar("ftp.teste.com.br", "the club", "teste123", 21);
         ClienteFTP.Upload(lstrArq, "/thiago"+lstrArq);
    }
    catch (Exception e)
     {
             trace("Erro : " + e.getMessage());
     }
  }



O método “Efetuar_Upload()” utiliza a classe_FTP. Selecionaremos o item desejado no “SpnListarArquivosCelular” (Os arquivos que estão no celular) e logo em seguida conectando no FTP chamando o método “Upload()”. Os parâmetros são respectivamente: Nome do arquivo que irá fazer o upload e Nome da pasta onde será salvo o arquivo no servidor FTP.



  public void Upload_click(View v)
  { 
dialog = ProgressDialog.show(AndroidFTPActivity.this,"The Club","Sincronizando Dados . . .", false, true);
    dialog.setCancelable(false);
     
    new Thread()
    {
       public void run()
       {
            try
            {
                 Efetuar_Upload();
                 dialog.dismiss();
            }
            catch(Exception e)
            {
                trace("Erro : " + e.getMessage());
            }
       }
    }.start();
   
    }



Este é o evento do botão “Upload()”, onde invocaremos a função criada no momento anterior. Faremos novamente o uso de Threads para execução de múltiplas tarefas e para dar uma melhor impressão ao usuário.



  public void toast (String msg)
  {
Toast.makeText (getApplicationContext(), msg, Toast.LENGTH_SHORT).show ();
  }
     
  private void trace (String msg)
  {
     toast(msg);
  }   
}



Métodos responsáveis para informar ao usuário de diversas ações que poderão ocorrer ao longo da execução do aplicativo.

A imagem 06 e a 07 nos dão uma noção básica de como ficou o aplicativo em funcionamento.




Imagem 06. Aplicação em Run-Time.





Imagem 07. Aplicativo efetuando Download/Upload de arquivos.




Conclusão


Procurei neste artigo fazer o uso simplificado e elegante do protocolo FTP, protocolo específico para transferência de arquivos via internet. Ao decorrer no desenvolvimento de aplicações existem diversas formas de implementar o uso deste mecanismo, tais como atualizador automático de executáveis, banco de dados, geração de scripts etre outros. Fica a critério do programador fazer ou não o uso desta tecnologia que busca sempre a automatização de tarefas. Com o Sistema Android isto também se torna possível e fácil, junto com bibliotecas de terceiros, que neste caso estamos usando a “commons-net” do Apache. Com o Sistema Android tudo se torna mais flexível pelo fato de ser uma mistura da linguagem XML com Java possibilitando a resolução de diversas situações.

Fica aí a dica amigos, Um forte abraço e até o mês que vem!


Sobre o Autor

Thiago Cavalheiro Montebugnoli adora aprender novas tecnologias. É tecnólogo, formado pela Faculdade de Tecnologia de Botucatu - SP (FATEC) foi consultor técnico do The Club, já desenvolveu softwares utilizando a plataforma .NET, Delphi junto com Banco de Dados SQL Server e Firebird. Atualmente trabalha no Centro de Processamento de Dados da Prefeitura Municipal de Itaí-SP e é colunista mensal da Revista The Club Megazine. Possui as seguintes certificações: MCP - Microsoft Certified Professional, MCTS - Microsoft Certified Technology Specialist, MCAD - Microsoft Certified Application Developer e MCSD - Microsoft Certified Solution Developer.

E-mail: thiago@theclub.com.br

The Club - O Maior Clube de programadores do Brasil