DevSecOps

21 jul, 2009

Realizando download e upload de arquivos com BlazeDS

Publicidade

Salve, pessoal.

Neste artigo irei falar e demonstrar como utilizar um recurso do Flash Player (FP) 10 onde podemos realizar download e upload de arquivos utilizando os recursos da classe FileReference em conjunto com RemoteObjects (BlazeDS/LifeCycle). Nas versões antigas do FP tínhamos de utilizar servlets para as operações de Down e Up. Mas como na informática tudo evolui para melhor, o Flash Player veio com algumas diferenças bacanas.

Para este artigo irei criar dois componentes bases, um para Upload e outro para Download. O primeiro irá selecionar arquivos e enviar via RemoteObject um ByteArray para o Server armazená-lo. O segundo lerá todos os bytes array armazenados e montar um DataGrid para o usuário escolher um para Download. Basicamente é isso. Apenas para ficar claro, a parte Server não terá nada de complexo, apenas métodos para o RemoteObjetc consumir e salvar os BytesArray em um diretório temporário. Assim, não irei abordar a parte de criação de um DataBase com colunas Blob. Talvez este possa até ser um artigo futuro, mas no momento ficaremos com este simples. Também não entrarei muito em detalhes sobre FileReference, pois aqui espero que você já saiba o básico.

Requisitos para realização do Artigo:

  • FlexBuilder ou Similar.
  • SDK 3.2 ou superior.
  • Compilar o projeto na versão FP 10.0.0
  • Noção mínima de BlazeDS e integração com Java.
  • Noção de Mxml e Ascript3

Então, mãos à obra.

Passo 1

Criar o projeto Flex, alterando o “Required Flash Player version” caso necessário. Para isso clique com o botão direito do mouse no projeto e vá até a aba “Flex Compiler”.


 

Passo 2

Vamos criar o seguinte pacote: “com.imaster”. Neste pacote iremos criar as seguintes classes:

PanelUpload.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical" width="100%" title="Upload"
paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20">


<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

private var refUploadFile:FileReference;

/**
* @private
* Chamado quando o botao de adicinar arquivor for clicado.
*/
private function adicionarArquivos():void {
/* Inicializa o FileReference a adicinar os Listeners */
refUploadFile = new FileReference();
refUploadFile.browse();
refUploadFile.addEventListener(Event.SELECT, trateArquivoSelecionado);
refUploadFile.addEventListener(Event.COMPLETE, trateArquivoCompleto);
}

/**
* @private
* Chamado quando o usuario selecinar o arquivo na popup.
*/
private function trateArquivoSelecionado(event:Event):void {
/* Este metodo LOAD, foi implementado na versao do FP 10,
* é graças a ele que conseguimos recuperar o ByteArray e enviar para o Server */
refUploadFile.load();
}

/**
* @private
* Chamado quando o usuario selecinar o arquivo na popup.
*/
private function trateArquivoCompleto(event:Event):void {
/* Pega a referencia do FileReference */
refUploadFile = event.currentTarget as FileReference;

/* Variavel para armazenar o ByteArray */
var data:ByteArray = new ByteArray();

/* readBytes é responsavel por ler logicamente os dados do FileReference
* e repassar o valor do ByteArray do arquivo para a variavel armazenadora 'DATA' */
refUploadFile.data.readBytes(data, 0, refUploadFile.data.length);

uploadService.doUpload(data, refUploadFile.name);
}

/**
* @private
* Exibe a mensagem ao usuario informando que o Upload foi realizado com sucesso.
*/
private function uploadSucesso(event:ResultEvent):void {
Alert.show("Arquivo salvo no server com sucesso", "Sucesso");
}

/**
* @private
* Exibe a mensagem de erro do motivo do Upload não ter sido realizado com sucesso.
*/
private function uploadErro(event:FaultEvent):void {
Alert.show(event.toString(), 'Erro');
}
]]>
</mx:Script>

<mx:RemoteObject id="uploadService" destination="gerenciadorArquivos" showBusyCursor="true"
result="uploadSucesso(event)"
fault="uploadErro(event)"/>

<mx:Button toolTip="Adicionar arquivos para download." click="adicionarArquivos()"
label="Selecinar Arquivo"/>
</mx:Panel>

A classe upload é muito simples. Possuindo:

  1. Um RemoteObject para o Server consumir, onde serão tratados os eventos de erro e sucesso.
  2. Um botão que executa o método adicionarArquivos(). Este, por sua vez também relativamente simples, apenas instancia um objeto FileReference adiciona dois listener SELECT e COMPLETE. O principal aqui neste fluxo é o método fileReference.browser() que é responsável por abrir uma popup para o usuário selecionar um arquivo para upload.
  3. O listener SELECT fica esperando o usuário selecionar um arquivo. Agora aqui é o primeiro passo importante: neste fluxo é executado apenas um método refUploadFile.load() e esta é a novidade do FP 10, pois é este o responsável por carregar o arquivo para a memória.
  4. NOTA: No FP 10 não há muitas restrições no tamanho dos arquivos para upload, mas oficialmente o FP suporta até 100mb. Sempre lembrando que para executar este método, antes é necessária a execução do método “browser()”.
  5. Por fim, o listener COMPLETE. Este é o segundo passo importante, pois após o LOAD ser executado com sucesso este listener é chamado. Neste momento nós já temos varias informações a respeito do arquivo que o usuário selecionou, como Nome e Tamanho. Mas antes de executar o método RemoteObject ‘doUpload’ devemos converter o arquivo em ByteArray, para isso a classe FileReference tem o método “readBytes()”. Ele lê o número de Bytes, pelo parâmetro “length” assim convertendo-o em um ByteArray.
  6. Após executado o método “readBytes()” podemos então enviar para o servidor.
PanelDownload.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="100%"
height="100%" title="Arquivos para Download">

<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.collections.ArrayCollection;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.AsyncToken;

private var fileReference:FileReference;
private var listaTemp:ArrayCollection;

/**
* @private
*/
private function downloadSucesso(event:ResultEvent):void {
listaTemp = new ArrayCollection();
for each(var objAux:Object in event.result){
listaTemp.addItem({nomeArquivo:objAux.nome, byteArray:objAux.byteArray});
}
listaArquivos.dataProvider = listaTemp;
}

/**
* @private
*/
private function downloadErro(event:FaultEvent):void {
Alert.show(event.toString(), 'Erro');
}

/**
* @private
* Pega Byte Array recuperado anteriormente e executa o metodo SAVE do fileREference
* no qual este irá converter o ByteArray no arquivo.
*/
private function salvarAquivo(event:Event):void {
if(listaArquivos.selectedItem){
fileReference = new FileReference();
fileReference.save(listaArquivos.selectedItem.byteArray,
listaArquivos.selectedItem.nomeArquivo);
}else
Alert.show('Selecione um arquivo');
}

/**
* @private
* Envia uma chamada ao server para recuperar a lista de arquivos salvos.
*/
private function retorneListaArquivosSalvos(event:Event):void {
downloadService.getListaDownload();
}
]]>
</mx:Script>


<mx:RemoteObject id="downloadService" destination="gerenciadorArquivos"
showBusyCursor="true" result="downloadSucesso(event)"
fault="downloadErro(event)"/>

<mx:DataGrid id="listaArquivos" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn headerText="Nome Arquivo" width="150" dataField="nomeArquivo"
wordWrap="true"/>
</mx:columns>
</mx:DataGrid>

<mx:ControlBar horizontalAlign="center" verticalAlign="middle">
<mx:Button toolTip="Lista todos os arquivos salvos pelo server."
label="Listar Arquivos Salvos"
click="retorneListaArquivosSalvos(event)"/>
<mx:Button toolTip="Salva o arquivo selecinado." width="150"
click="salvarAquivo(event)" label="Download Arquivo"/>
</mx:ControlBar>



</mx:Panel>

Esta classe, a meu ver, é até mais simples que a de Upload por não haver muitos detalhes específicos. Este componente contém:

  1. Um DataGrid para exibir todos os arquivos salvos no servidor.
  2. Dois botões, “Listar Arquivos Salvos” para listar todos os arquivos e o segundo “Download Arquivo” para realizar o download para maquina do usuário.
  3. O botão “Listar Arquivos Salvos” executa uma chamada via Remote para o Server, este por sua vez retorna uma lista de ArquivosVO (falarei mais adiante sobre a mesma).
  4. Após o método do Server ser executado com sucesso, o listener de sucesso do Remote é chamado, este apenas define o dataProvider do DataGrid com a lista de ArquivosVO.
  5. O botão “Download Arquivo” executa o metodo “SalvarArquivo()”, este é o único ponto importante deste componente. Pois ao ser executado o mesmo verifica se o usuário selecionou algum arquivo do Grid, caso sim é então instanciado um objeto FileReference e este por sua vez executa o metodo “SAVE()”, este recebe dois argumentos um é o byteArray do arquivo e o outro é o nome que será utilizado para referenciar o arquivo (contudo o mesmo pode ser mudado pelo usuário, já que o metodo save() abre uma popup para o usuário escolher o local para salvá-lo).
  6. Buummmm, aqui está feita toda a parte Flex para a realização do upload de arquivos, vamos agora para os detalhes da classe Java.

Passo 3

Neste  passo devemos criar um projeto “Java Web Dymanic”. Neste momento vou supor que você esta totalmente ambientado em como criá-lo e como realizar a integração, caso tenha alguma dúvida veja este tutorial.

Então vamos à classe Java responsável por armazenar os arquivos selecionados pelo usuário.

GerenciadorArquivos.java
package com.imaster;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

public class GerenciadorArquivos {

public GerenciadorArquivos() { }

public void doUpload(byte[] bytes, String fileName) throws Exception {
/* Pega o caminho de dirtorio temporario e concatena com o nome do arquivo */
fileName = System.getProperty("java.io.tmpdir") + fileName;
File arquivo = new File(fileName);
FileOutputStream fos = new FileOutputStream(arquivo);
fos.write(bytes);
fos.close();
}

public List<ArquivoVO> getListaDownload() throws Exception{
File dir = new File(System.getProperty("java.io.tmpdir"));
String[] children = dir.list();
List<ArquivoVO> resposta = new ArrayList<ArquivoVO>();

if (children != null) {
for (int i = 0; i < children.length; i++) {
resposta.add( new ArquivoVO(children[i], returnByteArray(children[i])) );
}
}
return resposta;
}

/**
* Retorna o ByteArray de um arquivo baseado no nome do mesmo.
* @param fileName
* @return
* @throws Exception
*/
private byte[] returnByteArray(String fileName) throws Exception{
byte[] data = null;
try {
FileInputStream fis = new FileInputStream(System.getProperty("java.io.tmpdir") + fileName);
FileChannel fc = fis.getChannel();
data = new byte[(int) (fc.size())];
ByteBuffer bb = ByteBuffer.wrap(data);
fc.read(bb);
return data;
} catch (Exception e) {
throw new Exception(e);
}
}
}
ArquivoVO.java
package com.imaster;

public class ArquivoVO {

private String nome;
private byte[] byteArray;

public ArquivoVO(String nome, byte[] byteArray) {
this.nome = nome;
this.byteArray = byteArray;
}

/**
* @return the nome
*/
public String getNome() {
return nome;
}
/**
* @param nome the nome to set
*/
public void setNome(String nome) {
this.nome = nome;
}
/**
* @return the byteArray
*/
public byte[] getByteArray() {
return byteArray;
}
/**
* @param byteArray the byteArray to set
*/
public void setByteArray(byte[] byteArray) {
this.byteArray = byteArray;
}


}

Esta classe possui apenas três métodos sendo eles:

  1. doUplaod: recebe dois parâmetros, um o byteArray do arquivo e outro o nome do arquivo. O fluxo deste metodo é pegar a referência da localização da sua pasta de arquivos temporários do Windows e então escrever os Bytes na pasta utilizando a classe FileOutputStream.
  2. getListaDownload: devolve uma lista de ArquivoVO para o Flex. Esta pojo ArquivoVO contem apenas duas propriedades, nome e byteArray.
  3. returnByteArray: é o metodo que recupera o arquivo salvo e o converte em byteArray.

Como disse anteriormente, não vou entrar em detalhes no java, pois presumo que você já tenha uma familiaridade com o mesmo, mas qualquer dúvida a respeito pode entrar em contato por email ou comentar sua dúvida aqui.

Pessoal, é isso, aqui está demonstrado de uma forma simples e eficaz como realizar operações de Download e Upload utilizando os recursos do Flash Player 10.

Download dos arquivos fonte.