terça-feira, 13 de novembro de 2012

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.


Figura 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.

Figura 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.

Figura 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.

Figura 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”.

Figura 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.

Figura 06: Aplicação em Run-Time.

Figura 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!


Nenhum comentário:

Postar um comentário