FileReferenceList - Upload de múltiplos arquivos

Algumas vezes em nossos projetos precisamos fazer uploads de mais de um arquivo.

No Flash a classe FileReferenceList fornece recursos para permitir que o usuário selecione um ou mais arquivos para upload. Um objeto FileReferenceList uma lista de FileReference. Isso significa que ao selecionar vários arquivos o método fileList possuirá a lista de Filereference, um para arquivo que você selecionou.

No exemplo abaixo, ao selecionar os arquivos, a lista é passada para o TileList e este gerencia as listas. No TileList criei um Itemrenderer com um Label, um ProgressBar e imagens para mostrar o status do upload. No main.mxml gerenciamos toda a aplicação, chamando o próximo upload assim que o primeiro se encerar. Assim que o Upload inicia a barra de progresso aparece e este desaparece automaticamente assim que o upload for concluindo.

Arquivo main.mxml
<mx:Script>
  <![CDATA[
    import mx.controls.Alert;

    // Instancia da classe FileReferenceList
    private var files:FileReferenceList = new FileReferenceList()
    // número do Filereference que esta sendo realizado o Upload.
    private var uploadAtual:int = -1;
    private function init ():void
    {
      files.addEventListener( Event.SELECT, selectHandler );
    }
    private function selecionar ():void
    {
      var tipos:FileFilter = new FileFilter( 'Somente Imagens',
                                             '*.jpg;*.jpeg;*.gif;*.png' )
      files.browse( [ tipos ] )
    }
    private function selectHandler ( event:Event ):void
    {
      lista.dataProvider = files.fileList
      lista.height = files.fileList.length * 29
      btEnviar.visible = true
      btSeleciona.visible = false
    }
    private function cancelHandler ():void
    {
      ( lista.dataProvider[ uploadAtual ] as FileReference ).cancel()
      btCancela.visible = false
    }
    private function enviarHandler ():void
    {
      proximoUpload()
      btEnviar.visible = false
      btCancela.visible = true
    }
    private function proximoUpload ():void
    {
      uploadAtual++;

      var url:String = "upload.php"
      var request:URLRequest = new URLRequest( url );
      var file:FileReference = ( lista.dataProvider[ uploadAtual ] as FileReference )
      file.addEventListener( Event.COMPLETE, uploadCompleteDataHandler )
      file.upload( request, 'imagem' )
    }
    private function uploadCompleteDataHandler ( event:Event ):void
    {
      if ( lista.dataProvider.length == uploadAtual )
      {
        Alert.show( "Todos Upload concluidos" )
        btCancela.visible = false
      }
      else
        proximoUpload()
    }
  ]]>
</mx:Script>
<mx:Canvas x="10"
           y="10"
           height="400"
           width="534"
           borderThickness="1"
           borderStyle="solid"
           backgroundColor="#CFCFCF"
           verticalScrollPolicy="on">
    <mx:TileList id="lista"
                 width="515"
                 height="100%"
                 labelField="name"
                 columnWidth="{lista.width}"
                 itemRenderer="com.eduardokraus.renderer.ListaRenderer"
                 backgroundAlpha="0"
                 horizontalScrollPolicy="off"
                 verticalScrollPolicy="off"
                 borderThickness="0" />
</mx:Canvas>
<mx:VBox x="552" y="10">
  <mx:Button id="btSeleciona"
             label="Selecione os arquivos"
             click="selecionar()"
             includeInLayout="{btSeleciona.visible}" />
  <mx:Button id="btEnviar"
             label="Enviar arquivos"
             width="148"
             click="enviarHandler()"
             visible="false"
             includeInLayout="{btEnviar.visible}" />
  <mx:Button id="btCancela"
             label="Cancelar"
             width="148"
             click="cancelHandler()"
             visible="false"
             includeInLayout="{btCancela.visible}" />
</mx:VBox>

Ao clicar em selecionar, é chamado o método browser do FileReferenceList que abre uma caixa de dialogo do seu SO para você selecionar arquivos. O filtro serve para você designar um filtro de extensão, para somente certas extensões serem selecionada. Assim que da caixa de dialogo você clicar em Abrir, o método selectHandler do main.mxml e este passa a lista o TileList. O Botão enviar passa a ser visível e o botão selecionar invisível.

Ao clicar em enviar, o botão enviar é ocultado e o botão cancelar é apresentado. E chama o método proximoUpload que envia os itens ao servidor.

Arquivo com/eduardokraus/renderer/ListaRenderer.mxml
<mx:Script>
  <![CDATA[
    import flash.net.navigateToURL;

    private var file:FileReference
    private var fileSalvo:String = ''

    override public function set data ( value:Object ):void
    {
      super.data = value

      if ( value is FileReference )
        file = value as FileReference
      file.addEventListener( Event.OPEN, initUploadHandler )
      file.addEventListener( DataEvent.UPLOAD_COMPLETE_DATA, uploadCompleteDataHandler )
      file.addEventListener( IOErrorEvent.IO_ERROR, errorHandler );
      file.addEventListener( SecurityErrorEvent.SECURITY_ERROR, errorHandler );
      file.addEventListener( Event.CANCEL, cancelHandler )
    }
    private function initUploadHandler ( event:Event ):void
    {
      progresso.visible = true
    }
    private function uploadCompleteDataHandler ( event:DataEvent ):void
    {
      if ( event.data == 'erro' )
        error.visible = true;
      else
      {
        fileSalvo = event.data.toString()
        sucess.visible = true;
      }
    }
    private function errorHandler ( event:Event ):void{}
    private function visualizar ():void
    {
      navigateToURL( new URLRequest( 'imagens/' + fileSalvo ) )
    }
    private function cancelHandler ( event:Event ):void
    {
      error.visible = true;
      progresso.visible = false
    }
  ]]>
</mx:Script>
<mx:Label x="5"
          text="{data.name}"
          verticalCenter="0" />
<mx:ProgressBar id="progresso"
                visible="{ false }"
                verticalCenter="0"
                right="5"
                labelPlacement="center"
                width="295"
                label="Carregando %1 de %2 KB (%3%%)"
                conversion="1024"
                source="data">
  <mx:completeEffect>
    <mx:Parallel>
      <mx:Fade alphaTo="0.0" />
        <mx:Zoom zoomHeightTo="0" />
    </mx:Parallel>
  </mx:completeEffect>
</mx:ProgressBar>
<mx:HBox id="sucess"
         right="5"
         visible="{ false }"
         click="visualizar()"
         verticalCenter="0"
         verticalAlign="middle">
  <mx:Label text="Clique para Visualizar"
            fontWeight="bold"
            color="#BB0404" />
  <mx:Image useHandCursor="true" source="@Embed('assets/success-icon.png')" />
</mx:HBox>
<mx:Image id="error"
          right="5"
          visible="{ false }"
          source="@Embed('assets/error-icon.png')"
          height="24"
          verticalCenter="0" />

Primeiro eu sobrescrevo o método data do Container para receber as alterações de valor do ItemRenderer do TileList. Quando recebo estas atualizações adiciono escuta aos eventos que fazem com que meu componente funcione.

Você agora deve estar se perguntando porque não tem a escuta do evento do ProgressEvent para gerenciar a barra de progresso. Simples, barra de progresso se gerencia automaticamente através do método source do ProgressBar.

Quando os dados retornados do servidor retornam, o método uploadCompleteDataHandler é executado e nele temos o dado retornado pelo servidor. Este dado retornado pode ser um simples echo. Se o valor retornado for “erro” mostramos a imagem de erro. Caso não for erro, mostramos a imagem de sucesso em um link para visualizarmos a imagem que foi feito upload.

Arquivo upload.php
<?php
$arquivo = $_FILES['imagem']['tmp_name'];
$nome = $_FILES['imagem']['name'];
$novoNome = md5(microtime());
$destino = 'imagens/' . $novoNome; 

// Pega a extensão
$extensao = strrchr( $nome, '.' );

// Converte a extensão para minúsculo
$extensao = strtolower($extensao);

// 'Somente imagens', '*.jpg;*.jpeg;*.gif;*.png'
if( strstr( '*.jpg;*.jpeg;*.gif;*.png', $extensao ) )
{
  move_uploaded_file( $arquivo, $destino . $extensao  );
  echo $novoNome . $extensao;
}
else
  echo 'erro';
?>

Este recebe o pedido de upload e antes analisa se este pode ser salvo. Para isso valida se a extensão do arquivo enviado esta na lista de extensões, se sim, move para a pasta image e retorna o novo nome para o Flex. Se não esta na lista apenas retorna a palavra “erro“.

Veja abaixo um exemplo funcionando.

Fonte disponível aqui.

Fique por dentro de nossas novidades, ideias e atualizações