Dae pessoal!

Depois de bastante tempo estou postando, como prometido, a última versão da minha solução de upload múltiplo de arquivos baseada na galeria de fotos do Orkut. Desta vez criei uma classe bastante simples em AS3 com interação completa dos eventos de upload com Javascript e muito mais fácil de implementar. Para quem está visualizando meu blog pela primeira vez, recomendo dar uma olhada nos posts anteriores: Upload múltiplo de arquivos e Upload múltiplo de arquivos com barra de progresso.

upload_multiplo

http://fredimachado.com.br/wp-content/plugins/downloads-manager/img/icons/actionscript.gif download: Uploader.as (12.58KB)
added: 09/06/2009
clicks: 1735
description: Classe AS3 para upload múltiplo de arquivos

O primeiro passo é salvar o arquivo Uploader.as em uma pasta e salvar lá também um documento Flash (Actionscript 3) e colocar a classe “Uploader” em “Document class:” na janela de propriedades do movie.

flash_properties

Feito isso você pode simplesmente colocar o texto “Adicionar Fotos”, ou até mesmo um botão, caso queira algum efeito rollOver, etc… Você não precisa colocar nenhuma action, pois a classe Uploader já vai adicionar o evento CLICK automaticamente. Agora é hora de começar o html e javascript:

Na tag HEAD vamos setar a url do script PHP de upload, carregar o jQuery, o plugin da barra de progresso e nosso “comum.js” (depois vou falar sobre ele). Ah, também tem o CSS para a listagem dos arquivos.

<script type="text/javascript">
	// Script de upload
	var url = "http://localhost/upload/upload.php";
</script>
 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="_js/jquery.progressbar.js"></script>
<script type="text/javascript" src="_js/comum.js"></script>
 
<style type="text/css">
	#arquivos { display:none; }
	.c30p { width:30%; float:left; }
	.c100 { width:100px; float:left; }
	.bold { font-weight:700; }
	.ac { text-align:center; }
	.sep { background-color:#EEE; height:1px; line-height:1px; clear:both; margin:1px; }
	#totais_arquivos { display:none; }
</style>

Agora adicionamos o SWF (testado com FF 3, Chrome, IEs 6, 7 e 8) e um código básico para a listagem dos arquivos:

<div>
	<script type="text/javascript">
		// Para M$ IE
		if (document.all)
		{
			document.write('<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="137" height="27" id="uploader" align="middle">');
			document.write('<param name="allowScriptAccess" value="sameDomain" />');
			document.write('<param name="allowFullScreen" value="false" />');
			document.write('<param name="menu" value="false" />');
			document.write('<param name="movie" value="_swf/upload.swf" />');
			document.write('<param name="quality" value="high" />');
			document.write('<param name="bgcolor" value="#ffffff" />');
			document.write('<embed src="_swf/upload.swf" quality="high" bgcolor="#ffffff" width="137" height="27" name="uploader" align="middle" allowScriptAccess="sameDomain" allowFullScreen="false" menu="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />');
			document.write('</object>');
		}
		else // Outros navegadores
			document.write('<embed type="application/x-shockwave-flash" src="_swf/upload.swf" id="uploader" name="uploader" bgcolor="#ffffff" quality="high" allowscriptaccess="always" menu="false" width="137" height="27" />');
	</script>
</div>
 
<div id="arquivos">
	<div>
		<div class="c30p bold">Foto</div>
		<div class="c100 ac bold">Tamanho</div>
		<div class="c100 ac bold">Remover</div>
		<div class="c30p bold">Andamento</div>
	</div>
	<div id="lista_arquivos"></div>
	<div id="totais_arquivos">
		<span class="bold">
			Total de fotos: <span id="total_arquivos"></span>.
			Tamanho total: <span id="total_tamanho"></span>.
		</span>
		<br />
		<input type="button" id="btUpload" value="ENVIAR" style="padding:5px;font-size:12px;" />
	</div>
</div>

O próximo passo é o código do comum.js, onde fazemos toda a comunicação com o SWF, ele está bem comentado, então só vou colocar o código aqui:

var uploader = null; // Objeto do SWF
var arquivos = {}; // Objeto com as informações dos arquivos (id, nome, tamanho, etc...)
var fila = []; // Array com os ids dos arquivos na fila
var atual    = 0; // Arquivo enviado no momento
var enviando = false; // Para evitar envio "duplo"
 
/**
 * Função que o Flash vai executar por padrão quando estiver tudo carregado
 * Assim podemos adicionar os listeners para nos retornar as informações
 */
function uploaderPronto()
{
	// Objeto do SWF no html
	uploader = document.getElementById('uploader');
 
	// Adiciona as funções
	// O primeiro argumento é o evento no Flash, o segundo argumento é a função
	// que o Flash irá executar aqui no Javascript quando o evento for disparado
	uploader.addListener('onSelected', 'onSelect');
	uploader.addListener('onProgress', 'onProgress');
	uploader.addListener('onComplete', 'onComplete');
	uploader.addListener('onCompleteData', 'onCompleteData');
 
	// Seta a url do script de upload
	uploader.url(url);
 
	// Seta os tipos de arquivos permitidos na caixa de seleção
	// Neste caso queremos somente imagens
	uploader.setaTiposPermitidos([
		{desc: "Imagens JPG, GIF ou PNG", ext: "*.jpg;*.jpeg;*.gif;*.png"}
	]);
}
 
// Escuta o evento "onSelected" do Flash
function onSelect(e)
{
	// Retorna uma array de objetos com informações do(s) arquivo(s) selecionado(s)
	var arqs = e.arquivos;
 
	// Faz um loop para adicionar os arquivos e criar as divs e barra de progresso
	for (var i = 0; i < arqs.length; i++)
		adicionaArquivo(arqs[i]);
 
	// Como setei no css a div #arquivos como "display:none"
	// agora quero que ela seja exibida
	$("#arquivos").show();
 
	// Executa a função para calcular totais (arquivos selecionados e tamanho total do upload)
	calculaTotais();
 
	// Seta o evento "click" do botão Enviar
	$("#btUpload").click(function() {
		enviarArquivos();
	});
}
 
// Adiciona o objeto com as informações do arquivo
function adicionaArquivo(arq)
{
	// Seta o objeto
	arquivos[arq.id] = arq;
 
	// Adiciona o html necessário para exibição das informações do arquivo e barra de progresso
	$('<div class="arquivo" id="arquivo_'+arq.id+'"><div class="c30p">'+arq.nome+'</div><div class="c100 ac">'+tamanho(arq.tamanho)+'</div><div class="c100 ac"><a href="javascript:void(0);" class="remover" rel="'+arq.id+'">X</a></div><div class="c30p progresso">&nbsp;</div><div class="sep" /></div>').appendTo("#lista_arquivos")
		.find(".progresso").append($('<span id="upload_'+arq.id+'" />').progressBar({ barImage: 'images/progressbg_green.gif' }));
 
	// Adiciona o evento "remover" ao clicar no link "X"
	$("#arquivo_"+arq.id+" .remover").click(function() {
		// Só continua caso o upload não esteja ativo
		if (!enviando)
		{
			// Pega o id, a partir do attributo "rel" do link
			var id = $(this).attr("rel");
			// Da um fadeOut e remove a div com as informações do arquivo
			$("#arquivo_"+id).fadeOut('fast', function() { $(this).remove(); });
			// Executa a função "removeArquivo" do Flash
			uploader.removeArquivo(id);
			// Remove as informações do arquivo do objeto
			delete arquivos[String(id)];
			// Re-calcula os totais
			calculaTotais();
		}
	});
}
 
// Função que é executada ao clicar no botão "Enviar"
function enviarArquivos()
{
	// Só continua caso o upload não esteja ativo
	if (!enviando)
	{
		// Começamos do primeiro arquivo da fila
		atual    = 0;
		// Agora estamos enviando
		enviando = true;
 
		// Monta a array fila com os ids dos arquivos
		for (var a in arquivos)
			fila.push(arquivos[a].id);
 
		// Executa a função "iniciaUpload" do flash já enviando o id do primeiro arquivo na fila
		uploader.iniciaUpload(fila[atual]);
	}
}
 
// Escuta o evento "onProgress" do Flash
function onProgress(e)
{
	// Calcula a porcentagem de acordo com as informações recebidas
	var valor = Math.ceil(Number(e.bytesLoaded / e.bytesTotal * 100));
	// Atualiza a barra de progresso
	$("#upload_"+e.id).progressBar(valor);
}
 
// Escuta o evento "onComplete" do Flash
function onComplete(e)
{
	// Vamos para o próximo arquivo
	atual++;
 
	// Se ainda não chegou ao final da fila, envia próximo arquivo
	if (atual < fila.length)
		uploader.iniciaUpload(fila[atual]);
	// Chegou ao final da fila, então aguarda 2 segundos e recarrega a página
	else
		window.setTimeout(function() {
			window.location.reload(true);
		}, 2000);
}
 
// Escuta o evento "onCompleteData" do Flash
function onCompleteData(e)
{
	// Caso o script retorne "1" é porque tudo ocorreu bem
	if (e.dados == "1")
		$("#upload_"+e.id).html("Enviado.");
	// Caso contrário é porque ocorreu algum erro
	else
		$("#upload_"+e.id).html("Erro ao enviar.");
 
}
 
// Calcula os totais (número de arquivos na fila e tamanho total dos arquivos)
function calculaTotais()
{
	var c = 0; // Quantidade de arquivos
	var t = 0; // Tamanho
 
	for (var i in arquivos)
	{
		c++;
		t += arquivos[i].tamanho; // Vai somando os tamanhos
	}
 
	// Temos pelo menos 1 arquivo na fila
	if (c)
	{
		$("#total_arquivos").text(c); // Número de arquivos
		$("#total_tamanho").text(tamanho(t)); // Tamanho total, já convertido para MB ou KB usando a função "tamanho"
		$("#totais_arquivos").show(); // Exibe a div
	}
	// Não temos arquivos
	else
	{
		// Esconde as divs
		$("#totais_arquivos").hide();
		$("#arquivos").hide();
	}
}
 
// Retorna o tamanho em MB ou KB
function tamanho(val)
{
	var kb = Number(Number(val)/1024).toFixed(1);
	return kb >= 1000 ? Number(kb/1024).toFixed(1) + " MB" : kb + " KB";
}

Tentei fazer o código o mais comentado possível, para o entendimento ser mais fácil. De qualquer maneira quero fazer em breve adocumentação da classe Uploader e postar um screencast sobre o assunto.

Todos os arquivos do post:

http://fredimachado.com.br/wp-content/plugins/downloads-manager/img/icons/winrar.gif download: uploader.zip (22.73KB)
added: 09/06/2009
clicks: 3138
description: Upload múltiplo de arquivos com a classe Uploader

Abraço e até a próxima!