Download de arquivos com Adobe AIR
Em um projeto recente em Flex/AIR, um player em um thin client precisava tocar vídeos, áudios e imagens que vinham de uma central. Havia a necessidade de fazer isso offline para que não torrasse banda. Então precisei de uma classe que faria o download de um arquivo, vindo de uma URL, para dentro da pasta de armazenamento da aplicação (applicationStoreDirectory):
package { import flash.events.Event; import flash.events.EventDispatcher; import flash.events.ProgressEvent; import flash.filesystem.File; import flash.filesystem.FileMode; import flash.filesystem.FileStream; import flash.net.URLRequest; import flash.net.URLStream; import flash.utils.ByteArray; public class FileDownloader extends EventDispatcher { // url do arquivo private var fileURL:String; // url stream do arquivo private var urlStream:URLStream; // file stream do arquivo private var fileStream:FileStream; // referencia fisica do arquivo completo private var fileRef:File; public function FileDownloader(){ // inicia os streams urlStream = new URLStream(); fileStream = new FileStream(); // eventos da URL urlStream.addEventListener(Event.COMPLETE, this.onComplete); urlStream.addEventListener(ProgressEvent.PROGRESS, this.onProgress); } public function download(url:String):void{ // referencia o caminho do arquivo fileURL = url; // abre o filestream do arquivo urlStream.load(new URLRequest(fileURL)); } private function onProgress(ev:ProgressEvent):void{ // para uso de barra de progresso, porcentagem, etc. this.dispatchEvent(ev); } private function onComplete(ev:Event):void{ // separa o nome do arquivo var urlParams:Array = fileURL.split("/"); urlParams.reverse(); // cria a referencia dos arquivos fileRef = File.applicationStorageDirectory.resolvePath(urlParams[0]); // abre o arquivo para escrita fileStream.open(fileRef, FileMode.WRITE); var byteArray:ByteArray = new ByteArray(); urlStream.readBytes(byteArray, 0, urlStream.bytesAvailable); // escreve o arquivo temporario fileStream.writeBytes(byteArray, 0, byteArray.length); // fecha as streams urlStream.close(); fileStream.close(); // dispara o evento this.dispatchEvent(new Event(Event.COMPLETE)); } } }
Para usá-la, foi bem simples:
var fileDownloader:FileDownloader = new FileDownloader(); fileDownloader.addEventListener(Event.COMPLETE, this.downloadComplete); fileDownloader.download('http://www.meusite.com/meuarquivo.jpg'); function downloadComplete(ev:Event):void{ // download completo }
Apenas justificando porque não usei URLLoader ao invéis de URLStream: porque apesar de não ter sido implementado, a classe URLLoader não me daria os bytes já carregados do meu arquivo para que (caso quisesse), fosse escrevendo o arquivo a medida que ele fosse baixado.
Zend Framework – Extendendo classes
O Zend Framework é cheio de recursos (na maior parte das vezes, sobra), mas não quer dizer que estes sejam 100% ajustados a nossa necessidade. Quando nós olhamos pra fora (outros frameworks, linguagens), achamos coisas que gostaríamos que houvessem lá, mas, por enquanto, não existem. Solução: extendemos as classes!
Uma defesa clássica sobre extender classe é: nunca mexa no core! Claro! Você não vai sujeitar algo com 1 zilhão de commits e equipes desenvolvedoras as suas mãos falhas e exaltadas (nossa, que drama). A classe mais extendida, por ser a mais usada, é a classe do modelo: Zend_Db_Table. Quem já mexeu com Rails ou Cake sabe que faltam triggers de inserção ou validação, entre outras coisas.
Lá vem as pedras dizendo "já que tem que fazer, por que não migra logo pro outro?". Bom, vou ser rápido e responder que não vou discutir sobre isso.
Um exemplo de extensão e adição de triggers:
class Frk_Db_Table extends Zend_Db_Table { // dados da tabela protected $_data; /** * insere os dados validando os filtros * @param $data dados a serem inseridos na tabela * @return Integer Chave primaria da linha inserida */ public function insert($data) { try { $this->_data = $data; // valida filtros de eventos anteriores if (method_exists($this,'before')) { $this->before(); } if (method_exists($this,'beforeInsert')) { $this->beforeInsert(); } // insere os dados e retorna a OK return parent::insert($this->_data); } catch (Exception $e){ // trata o erro return FALSE; } } }
Neste exemplo o método insert foi extendido para disparar dois triggers, befor e beforeinsert. Tomei esses dois exemplos vindo de outros frameworks. Muitas vezes são úteis. Segue um exemplo de implementação.
class Usuario extends Frk_Db_Table { protected $_name = 'usuarios'; private function beforeInsert(){ $this->_data['adicionado_em'] = date("Y-m-d"); } }
Sempre que for utilizado o método insert, a classe usuário adiciona o campo adicionado_em. É uma boa maneira de se economizar código, especialmente nas views, neste caso. Eu, particularmente, também sempre extendo a classe Zend_Controller_Action, com métodos que auxiliam durante os requests.
Novo layout, nova idéia…
Além de ter achado esse tema legal pra mudar a cara do blog, daqui pra frente pretendo manter uma idéia diferente: já que eu não estou com tempo pra ficar produzindo coisa pra cá ainda, além dos planos daqui incluirem os screencasts (em breve), vou continuar postando o que eu achar de relevante com explicação por cima, pra não ficar com cara de "twittada".
Bom é isso aí.
Zend_Date e MySQL Timestamp
Precisava colocar um campo daqueles clássicos "atualizado em", em Timestamp do MySQL, num projeto em ZF.
Voilá:
... $now = new Zend_Date(); $info['update_at'] = $now->get(Zend_Date::W3C); ...
Silverlight on Mac OS

Silverlight on Mac OS
Será que é tão difícil reconhecer o meu OS? E melhor, que tal um link direto? Eu simplesmente ainda não consegui fazer o dowload do plugin, afinal, ele só libera o .EXE