rbosa Crivelli continua escrevendo sobre AngularJS, com o ... · Ricardo Barbosa Crivelli continua...
Transcript of rbosa Crivelli continua escrevendo sobre AngularJS, com o ... · Ricardo Barbosa Crivelli continua...
Caro leitor,
Primeiramente gostaria de desejar um ótimo carnaval e que este mês de Fevereiro seja repleto de felicidade.
Nosso colunista Hamden Vogel redigiu o artigo “THVDataSet – DataSet baseado em Memória”, o qual
implementou rotinas práticas e velozes através do componente THVDataSet. Este componente é um descendente de
“TMemoryStream”, que permite armazenamento temporário de grandes quantidades de dados. Já nosso colaborador
Ricardo Barbosa Crivelli continua escrevendo sobre AngularJS, com o artigo entitulado “AngularJS - Single Page App“.
Com o avanço das aplicações web voltadas para smartphones as conhecidas como “Single Page Applications” se tornaram
cada dia mais comum. Essas aplicações são nada mais que uma página HTML que carrega o conteúdo sem que a página
seja recarregada dando assim a impressão de que o usuário está usando uma aplicação nativa e não navegando em um
site. O colunista Thiago C. Montebugnoli continua abordando temas voltados para a plataforma .Net junto com a
linguagem C#. O primeiro artigo ele finaliza a série de artigos sobre FTP de Arquivos, sendo que neste ele orienta e
desenvolve um exemplo prático de como deveremos efetuar o Download de Arquivos através de um servidor FTP. Já no
artigo “Linguagem C# - Utilizando o BackgroundWorker” ele nos ensina a trabalhar com este componente presente e
muito utilizado em Windows Forms, uma alternativa para quem precisa implementar o uso de Threads.
Desejo a todos uma boa leitura!
Marcos César Silva
Diretor Técnico
Editorial
Linguagem C# - Utilizando o BackgroundWorker
Uma das razões para fazermos o uso de “Threads” é quando desenvolvemos alguma rotina e encontramos uma
certa “demora” na execução de determinada tarefa. Podemos destacar alguns exemplos como: consulta em banco de
dados, acesso a um determinado serviço, leitura de qualquer tipo de arquivo, e consequentemente, esta tela fica
“travada” até que a tarefa se finalize, impedindo que o usuário acesse qualquer outro tipo de rotina. Para isto, existe o
componente “BackgroundWorker”, presente na linguagem C# junto com a plataforma Windows Forms. Neste artigo
iremos descrever algumas de suas principais funcionalidades.
Criando o exemplo
A origem do “BackgroundWorker” vem do namespace “System.ComponentModel” e o seu uso é muito fácil e
intuitivo, bastando arrastá-lo no formulário para visualizarmos os eventos e principais propriedades.
Ver Imagem 01.
Figura 01: Componente BackgroundWorker.
Antes de nos aprofundarmos em suas propriedades e métodos iremos montar a estrutura principal de nosso
exemplo prático. Para isto, iremos adicionar um “Backgroundworker” situado na palheta de componentes chamada
“Components”, uma barra de progresso “Progressbar” e mais dois botões denominados “Iniciar” para iniciar o progresso
e “Cancelar” para cancelar o progresso da tarefa. A estrutura do formulário deverá ficar idêntica ao da Imagem 02.
Figura 02: Exemplo Prático.
O componente “Backgroundworker” não é visual, portanto, o mesmo ficará situado na parte inferior de nossa
tela. Ver Imagem 03 para maiores detalhes.
Figura 03. BackgroundWorker.
Ao clicar sobre o componente podemos conferir algumas propriedades importantes, como:
WorkerReportsProgress: Propriedade booleana para indicar se o componente suporta a utilização de uma barra
de progresso.
WorkerSupportsCancellation: Propriedade Booleana para indicar o cancelamento da operação iniciada.
Ver Imagem 04.
Figura 04: Propriedades.
Faremos o uso de três principais eventos, sendo:
DoWork:
Esse evento é gerado quando iniciamos a operação que executa a rotina especificada. Temos o controle através deste
evento periodicamente sobre o valor da propriedade “CancellationPending” para cancelar a operação ou caso contrário para
continuar o processo normalmente.
ProgressChanged: Evento disparado para preencher a barra de progresso através da propriedade “e.ProgressPercentage”.
RunWorkerCompleted: Ocorre quando a operação foi concluída, cancelada ou gerou uma exceção.
Para visualizar estes eventos, verificar Imagem 05.
Figura 05: Eventos.
Codificando o exemplo
Antes de partirmos para codificação deveremos configurar para “true” as propriedades “WorkerReportsProgress” e
“WorkerSupportsCancellation”, para permitir a implementação da barra de progresso e suportar cancelamento de tarefas
respectivamente.
Adicione o seguinte namespace para trabalharmos com “Threads”
using System.Threading;
No método “backgroundWorker1_DoWork” iremos programar um laço simples, nos dando a impressão de
processamento de alguma rotina. Ele alimentará uma lista de inteiros e a barra de progresso através do método
“ReportProgress” simulará uma pausa de “0,5” segundos através da propriedade “Thread.Sleep”. Faremos também uma
verificação se o processo foi cancelado para em seguida abortar a operação. Ver Listagem 01.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
List<int> temp = new List<int>();
for (int i = 0; i <= 10; i++)
{
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
break;
}
backgroundWorker1.ReportProgress(i * 10);
Thread.Sleep(500);
temp.Add(i);
}
e.Result = temp;
}
Listagem 01.
No evento “backgroundWorker1_ProgressChanged”, alimentaremos a “progressbar” através da propriedade
“e.ProgressPercentage”.
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Para o método “backgroundWorker1_RunWorkerCompleted” e pelo argumento
“RunWorkerCompletedEventArgs” conseguimos analisar se o processo foi concluído ou
cancelado.
private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Rotina Cancelada!!");
}
else
{
MessageBox.Show("Rotina Completada!!");
}
}
Para o evento “Click” do botão “Iniciar” faremos uma verificação se o
“backgroundWorker” está executando alguma ação assíncrona, caso constrário
iniciaremos a operação com o método “backgroundWorker1.RunWorkerAsync()”.
private void btnIniciar_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
E para cancelar a operação invocaremos o método
“backgroundWorker1.CancelAsync()”.
private void btnCancelar_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
Na Imagem 06 podemos conferir o resultado do processo iniciado.
Figura 06: Rotina Completada.
Já na Figura 07 será cancelado o processo ao clicar no botão “Cancelar”. Note que a barra de progresso
também ficou paralisada no momento que desejamos, ou seja, toda a rotina foi abortada.
Figura 07: Rotina Cancelada.
Conclusões
Podemos aprender neste artigo todos os macetes necessários para trabalhar com o componente
“BackgroundWorker”.
Fique à vontade para implementar em seus sistemas da maneira que desejar.
Fica aí a dica deste mês.
Um abraço.
Referências
https://msdn.microsoft.com/pt-br/library/system.componentmodel.backgroundworker(v=vs.110).aspx
Sobre o Autor Thiago Cavalheiro Montebugnoli Adora aprender novas tecnologias. Formado
pela Faculdade de Tecnologia de Botucatu - SP (FATEC), já desenvolveu softwares utilizando a plataforma .NET, Delphi junto com Banco de Dados SQL Server e Firebird. Como experiências
profissionais mais recentes, possui em seu currículo sua atualização no Centro de Processamento de Dados da Prefeitura Municipal de Itaí-SP e atualmente coordena a equipe da Coordenadoria Tecnologia da Informação no IFSP - Instituto Federal do Estado de São Paulo em Avaré. Além
disso, é colunista mensal da Revista The Club Megazine e é consultor Técnico do The Club. Possui as seguintes certificações: MCP - Microsoft Certified Professional, MCTS - Microsoft Certified
Technology Specialist, MCAD - Microsoft Certified Application Developer e MCSD - Microsoft Certified Solution Developer.
E-mail: [email protected]
Single Page Application com AngularJS
Introdução Com o avanço das aplicações web voltadas para smartphones as conhecidas como Single Page Applications se
tornaram cada dia mais comum. Essas aplicações são nada mais que uma página HTML que carrega o conteúdo sem
que a página seja recarregada dando assim a impressão de que o usuário está usando uma aplicação nativa e não
navegando em um site. Antes de o Angular ser criado era muito comum que esse conteúdo fosse recarregado via requisições AJAX e
que toda chamada necessitava ser tratada manualmente gerando um trabalho muito grande que dificultava a
manutenção e os testes. No artigo de hoje nós iremos criar uma aplicação para uma banda com uma página inicial, uma página de
sobre e uma página de contato.
HTML Em nosso aplicativo nós iremos utilizar o Bootstrap, uma biblioteca desenvolvida pelo Twitter amplamente
utilizada no mundo inteiro que nos fornece várias facilidades.
Para começarmos nos iremos criar o nosso index.html que servirá como um esqueleto para toda a nossa aplicação.
Arquivo index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js">
</script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-
route.js"></script>
<script src="script.js"></script>
<link rel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
</head>
<body>
<header>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">The Club Band</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Início</a></li>
<li><a href="#sobre">Sobre</a></li>
<li><a href="#contato">Contato</a></li>
</ul>
</div>
</nav>
</header>
<div id="main">
<!-- aqui é o local onde as views serão -->
<!-- injetadas pelo Angular -->
</div>
</body>
</html>
Como é possível observar o arquivo é bastante simples. Logo no início nós utilizamos a biblioteca do
AngularJS e a diretiva responsável pelas rotas no Angular e depois o arquivo CSS com os estilos do Bootstrap.
No HTML existem dois pontos importantes, o primeiro é a nossa navbar que utiliza o estilo padrão do
Bootstrap, mais informações de como utilizar a navbar podem ser encontrados na documentação oficial no endereço
http://getbootstrap.com/components/#navbar. O segundo ponto importante é a div com o id main, é nela que o
Angular irá injetar o conteúdo de cada view.
É importante notar que os endereços dos links estão iniciando com #, isso é utilizado pois evita que o
navegador trate o link como um link comum e tente recarregar a nossa página.
Criação do Módulo e do Controller O passo seguinte será criarmos o nosso módulo e o controller responsáveis por iniciar nossa aplicação. Eles
serão criados no arquivo script.js que é chamado pelo nosso index.html. Vamos ao arquivo:
// Arquivo script.js
var theClubApp = angular.module('theClubApp', []);
theClubApp.controller('controllerPrincipal', function($scope) {
$scope.mensagemInicial = 'Bem-vindos a página da The Club Band!';
});
Se você tiver dúvida como o escopo do Angular funciona aconselho que você leia o artigo postado na edição
anterior da The Club intitulado de Introdução aos Escopos do AngularJS.
Se você testar a aplicação verá que nada acontece, isso é porque nós não iniciamos a theClubApp no
index.html, portanto vamos adaptá-lo um pouco. As mudanças estão em negrito.
Arquivo index.html
<!doctype html>
<html ng-app=”theClubApp”>
<head>
<meta charset="UTF-8">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js">
</script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-
route.js"></script>
<link rel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
</head>
<body ng-controller=”controllerPrincipal”>
<header>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">The Club Band</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Início</a></li>
<li><a href="#sobre">Sobre</a></li>
<li><a href="#contato">Contato</a></li>
</ul>
</div>
</nav>
</header>
<div id="main">
{{ mensagemInicial }}
<!-- aqui é o local onde as views serão -->
<!-- injetadas pelo Angular -->
</div>
</body>
</html>
Dentro de nossa div main nós incluímos a mensagem inicial e agora que a nossa aplicação e nosso módulos
foram instanciados vamos começar a trabalhar em como injetar o conteúdo das novas páginas da nossa aplicação.
Injetando as páginas em nosso esqueleto O ng-view uma diretiva responsável por injetar o HTML da rota selecionada em nosso esqueleto e exibir ao
navegador. Nós iremos adicionar a tag ng-view em nossa div main para dizer ao Angular onde injetar nossas páginas.
Arquivo index.html
...
</header>
<div id="main">
<!-- aqui é o local onde as views serão -->
<!-- injetadas pelo Angular -->
<div ng-view></div>
</div>
</body>
Configurando as rotas e as views
Como nossa página será uma Single Page Application nós não queremos que a página seja recarregada em
momento algum, para isso nós iremos utilizar a $routeProvider para que o Angular possa tomar conta de toda a
mágica. Vamos voltar ao nosso arquivo Java Script.
// Arquivo script.js
var theClubApp = angular.module('theClubApp', ['ngRoute']);
theClubApp.config(function($routeProvider){
$routeProvider
// rota para a página início
.when('/', {
templateUrl : 'templates/inicio.html',
controller : 'controllerPrincipal'
})
// rota para a página sobre
.when('/sobre, {
templateUrl : 'templates/sobre.html',
controller : 'controllerSobre'
})
// rota para a página contato
.when('/contato, {
templateUrl : 'templates/contato.html',
controller : 'controllerContato'
});
});
theClubApp.controller('controllerPrincipal', function($scope) {
$scope.mensagemInicial = 'Bem-vindos a página da The Club Band!';
});
theClubApp.controller('controllerSobre', function($scope) {
$scope.mensagemInicial = 'A The Club Band é uma banda fictícia criada para
o artigo de como criar uma Single Page Application utilizando o framework
AngularJS.';
});
theClubApp.controller('controllerContato', function($scope) {
$scope.mensagemInicial = 'Se você quiser nos contratar basta entrar em
contato com nosso empresário.';
});
Agora nós definimos nossas rotas através do $routeProvider, o Angular irá buscar o arquivo
inicio.html quando a sua rota for selecionada, o mesmo acontecerá com as outras rotas. Para finalizar este artigo
vamos criar os templates:
<!-- inicio.html -->
<div class="text-center">
<h1>Início</h1>
<p>{{ mensagemInicial }}</p>
</div>
<!-- sobre.html -->
<div class="text-center">
<h1>Sobre a The Club Band</h1>
<p>{{ mensagemInicial }}</p>
</div>
<!-- contato.html -->
<div class="text-center">
<h1>Contato</h1>
<p>{{ mensagemInicial }}</p>
</div>
Importante: As rotas no Angular somente funcionarão se você estiver rodando em um servidor, ou seja, se você estiver
testando a aplicação em seu computador local certifique-se que a aplicação rode na url http://localhost ou em outro
ambiente.
Conclusão
No artigo de hoje nós aprendemos como criar um Single Page Application de forma rápida e simples. Com o
conhecimento adquirido hoje será possível criar aplicações mais robustas e dinâmicas utilizando o framework
AngularJS.
Sobre o Autor
Ricardo Barbosa Crivelli, mais conhecido como Rico Crivelli, é formado como Bacharel
em Sistemas de Informação e Licenciado em Computação pela Universidade Estadual do Norte do Paraná,
atualmente é Técnico em TI no Instituto Federal de São Paulo – Câmpus Avaré. Tem como especialidade a linguagem PHP e o framework Symfony, apesar de adorar trabalhar com front-end e desenvolvimento
mobile e possuir as certificações COBiT 4.1 Foundation e Delphi 2006 Developer.
E-mail: [email protected]
Linguagem C# - Ftp de Arquivos – Parte 2
Caro amigo, neste mês irei explorar a segunda parte do artigo sobre FTP de arquivos utilizando a linguagem
C# junto com a plataforma Windows Forms. No mês passado criamos uma classe chamada “Ftp”, contendo o método
necessário para a realização do Upload de arquivos. Neste mês de Fevereiro irei ensiná-los a realizar o “Download”
de arquivos deste mesmo servidor. Para isto criarei uma interface implementando métodos desta mesma classe Ftp.
Utilizaremos as duas classes descritas no artigo de Janeiro, sendo a: “FtpWebRequest” e a
“FtpWebResponse”. Para maiores detalhes recomendo a leitura da primeira parte deste artigo.
Reutilizando a Classe Ftp
Iremos implementar mais dois métodos na classe FTP, sendo:
“ListarArquivos”: Responsável por trazer todos os arquivos contidos no servidor FTP, nos permitindo fazer a escolha
do arquivo a ser baixado.
“Download”: Rotina necessária para realizar o download do arquivo escolhido.
Deveremos também implementar mais alguns atributos, como: “ftpResponse”, “ftpStream” e “bufferSize”.
Ambos os métodos e atributos serão explicados a seguir.
Declarando os atributos necessários:
private FtpWebResponse ftpResponse = null;
private Stream ftpStream = null;
private int bufferSize = 2048;
Na função “Download” passaremos por parâmetros o caminho do arquivo contido no servidor FTP e na
máquina local. Teremos um retorno booleano para indicar se a rotina foi realizada ou não com sucesso.
Primeiramente instanciamos a classe “FtpWebRequest” fornecendo o endereço do servidor e local. Para o método
“Credentials” atribuímos o usuário e senha do FTP. Utilizaremos configurações como “UseBinary” para arquivos
binários, “UsePassive” para definir o comportamento do processo de transferência de dados de um aplicativo,
“KeepAlive” para especificar se a conexão de controle para o servidor FTP é fechada depois que a solicitação for
concluída. O próximo passo seria identificar o tipo de tarefa através do atributo “Method”.
Para isto escolheremos “DownloadFile” e logo em seguida estabelecemos um retorno do servidor FTP através
do método “GetResponse”. O atributo “ftpStream” irá nos auxiliar para testarmos o “Download” do arquivo através
de uma rotina “Try/Catch”.
Ver Listagem 01.
public bool Download(string remoteFile, string localFile)
{
try
{
ftpRequest = (FtpWebRequest)FtpWebRequest.Create(enderecoFtp + "/" +
remoteFile);
ftpRequest.Credentials = new NetworkCredential(usuario, senha);
ftpRequest.UseBinary = true;
ftpRequest.UsePassive = true;
ftpRequest.KeepAlive = true;
ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
ftpStream = ftpResponse.GetResponseStream();
FileStream localFileStream = new FileStream(localFile, FileMode.Create);
byte[] byteBuffer = new byte[bufferSize];
int bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);
try
{
while (bytesRead > 0)
{
localFileStream.Write(byteBuffer, 0, bytesRead);
bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);
}
}
catch
{
return false;
}
localFileStream.Close();
ftpStream.Close();
ftpResponse.Close();
ftpRequest = null;
}
catch
{
return false;
}
return true;
}
Listagem 1.
A rotina “Listararquivos” possui como parâmetro de entrada o diretório onde os arquivos se encontram no
servidor FTP. Ela nos retorna um Array de Strings, contendo todos os nomes dos arquivos do endereço fornecido.
Iniciamente utilizaremos exatamente as mesmas opções descritas no método “Download”. Usaremos o método
“WebRequestMethods.Ftp.ListDirectory” para listar os arquivos do diretório indicado. Iremos ler linha por linha
armazenando na variável “directoryRaw” e separada por um pipe “|”. Por final, retornaremos a variável “directoryList”
contendo todos os nomes dos arquivos.
Ver Listagem 02.
public string[] ListarArquivos(string directory)
{
try
{
ftpRequest = (FtpWebRequest)FtpWebRequest.Create(enderecoFtp + "/" +
directory);
ftpRequest.Credentials = new NetworkCredential(usuario, senha);
ftpRequest.UseBinary = true;
ftpRequest.UsePassive = true;
ftpRequest.KeepAlive = true;
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
ftpStream = ftpResponse.GetResponseStream();
StreamReader ftpReader = new StreamReader(ftpStream);
string directoryRaw = null;
try
{
while (ftpReader.Peek() != -1) {
directoryRaw += ftpReader.ReadLine() + "|"; }
}
catch { }
ftpReader.Close();
ftpStream.Close();
ftpResponse.Close();
ftpRequest = null;
try
{
string[] directoryList = directoryRaw.Split("|".ToCharArray());
return directoryList;
}
catch { }
}
catch { }
return new string[] { "" };
}
Listagem 02.
Criando um exemplo prático
Nosso formulário deverá conter a seguinte estrutura: 4 pares de Labels e TextBox sendo (Endereço Ftp,
Usuário, Senha e Download em), um Listbox contendo o descritivo “Arquivos”, onde serão adicionados os arquivos
contidos no servidor FTP e o botão “Download”, o qual fará o trabalho de baixar o arquivo no servidor FTP. Ver
Imagem 01.
Figura 01: Criando a interface gráfica para consumir a classe Ftp.
O método “RecuperarArquivosFTP”, que fará o trabalho de listar todos os arquivos do servidor FTP através
do método “ListarArquivos” contidos na classe FTP. Invocaremos o mesmo logo após o método
“InitializeComponent”, ou seja, quando carregamos os componentes na tela.
public FrmFTPDownload()
{
InitializeComponent();
RecuperarArquivosFTP();
}
private void RecuperarArquivosFTP()
{
Ftp ftp = new Ftp(txtEndereco.Text, txtUsuario.Text, txtSenha.Text);
string[] detailDirectoryListing = ftp.ListarArquivos("/thiago");
for (int i = 0; i < detailDirectoryListing.Count(); i++)
{
lstArquivos.Items.Add(detailDirectoryListing[i]);
}
}
No evento “Click” do botão “Download” usaremos a função “consistencias” (Implementada no artigo do mês
de Janeiro). Esta função terá algumas pequenas alterações, como por exemplo a inclusão do campo “Download em:”,
ver código abaixo:
if (txtDownload.Text.Length <= 0)
{
MessageBox.Show("O campo Download é de preenchimento obrigatório!",
"Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
Por este motivo não incluirei por inteiro a mesma em nosso artigo deste mês.
Receberemos o nome do arquivo através da seleção do mesmo no ListBox. Através da instância da classe
FTP passaremos para o método construtor os dados do endereço, usuário e senha e em seguida atribuímos para o
método “Ftp.Download” o caminho absoluto do arquivo localizado no servidor FTP e o caminho absoluto local.
Passaremos uma mensagem informando se o processo foi realizado com sucesso através do “MessageBox”.
private void btnDownload_Click(object sender, EventArgs e)
{
if (consistencias() == true)
{
String nomeArquivo = lstArquivos.SelectedItem.ToString();
Ftp ftp = new Ftp(txtEndereco.Text, txtUsuario.Text, txtSenha.Text);
if (ftp.Download("thiago/" + nomeArquivo, txtDownload.Text + nomeArquivo))
MessageBox.Show(string.Format("Download do arquivo {0} realizado com
sucesso!", nomeArquivo), "Informação", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
else{
MessageBox.Show(string.Format("Ocorreu uma falha no download do arquivo {0}!",
nomeArquivo), "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Podemos conferir o exemplo em Run-time na Figura 02.
Figura 02: Exemplo em Run-Time.
Conclusões
Nesta segunda e última parte aprendemos a implementar o uso de download de arquivos via FTP. Assim
completamos o ciclo completo das principais tarefas envolvendo FTP através da linguagem C#. Fica aí a dica, um
abraço e até o mês que vem!
Referências
https://msdn.microsoft.com/pt-br/library/system.net.ftpwebresponse(v=vs.110).aspx
https://msdn.microsoft.com/pt-br/library/system.net.ftpwebrequest(v=vs.110).aspx
Sobre o Autor
Thiago Cavalheiro Montebugnoli, adora aprender novas tecnologias. Formado pela Faculdade de
Tecnologia de Botucatu – SP (FATEC), já desenvolveu softwares utilizando a plataforma .NET, Delphi junto com Banco de Dados SQL Server e Firebird. Como experiências profissionais mais recentes, possui em seu currículo sua atuação no Centro de Processamento de Dados da Prefeitura Municipal de Itaí-SP e atualmente compõe a equipe da Coordenadoria Tecnologia da Informação no IFSP – Instituto Federal do Estado de São Paulo em Avaré. Além disso, é colunista mensal da Revista The Club Megazine e é consultor Técnico do The Club. Possui as seguintes certificações: MCP - Microsoft Certified Professional, MCTS - Microsoft Certified Technology Specialist, MCAD- Microsoft Certified Application Developer e MCSD - Microsoft Certified Solution Developer.
E-mail: [email protected]
THVDataSet – DataSet baseado em Memória
Interessante como existem várias alternativas para manipulação de dados em memória por parte do Delphi; o
que vamos dar foco neste artigo é a manipulação em grande escala de informações para os controles Data-Aware
utilizando desta vez um novo componente, mais flexível, batizado de THVDataSet. Neste artigo vamos ver sua utilização
e perceber como ele muito mais rápido que qualquer outro componente disponível opensource a ser disponibilizado
em uma Grid – e perceber também que está sendo lido e carregado tudo em memória – e porque não aproveitar as
vantagens de um objeto do tipo TMemoryStream para estas funcionalidades? Na edição anterior abordamos um componente descendente diretamente de TStream, onde podemos carregar
e salvar nossos registros (persistência) de uma forma muito transparente para o usuário por ser também descendente
de um TDataSet. O aplicativo tem ao seu lado um “banco de dados” muito prático e funcional – ilimitado o número de registros
– e também desenvolvemos uma funcionalidade original para o processamento de layout (header) para que nosso
banco pudesse ser aberto antes de ser lido. Agora no entanto queremos uma funcionalidade semelhante – mas sem
persistência – porque queremos que tudo seja carregado em memória e seja lido, editado, excluído, enfim, como um
descendente tradicional de um TDataSet – só que tudo em memória. Talvez a pergunta mais fácil e óbvia seria – “porque então não utilizamos um objeto de TClientDataSet que já
vem no Delphi?” – “ele também é baseado em memória!” – Sim, é verdade, é baseado em memória e já vem no Delphi,
desde a versão 3, porém o questionador destas perguntas não sei se já percebeu que este mesmo objeto de
TClientDataSet a qual se refere não é rápido assim. Se for para poucos registros, tudo bem! Mas não queremos para
poucos registros. Queremos testar na base dos 1.000.000 de registros em memória e com excelente resposta de rapidez!
E aí, o que dizer agora? A resposta intrigante para estas perguntas desafiadoras trago aqui como boa notícia! E digo mais, porque em
poucos segundos isso será real – será carregado um milhão de itens em um DBGrid associado a este nosso novo
componente – contrariando a lentidão do tradicional TClientDataSet que levará MINUTOS para executar a mesma
operação no mesmo TDBGrid. Vamos aos fatos? Boa leitura!
Comparativo de velocidade Vamos primeiramente abordar em uma tabela abaixo o quanto de tempo necessário para carregar
hipoteticamente uma coluna do tipo integer – o nome do atributo é “Cd_User”, e vamos inserir dinamicamente (através
de uma variável auxiliadora que fará o papel de incremento um a um) o conjunto de um milhão de itens inteiros. O
objetivo deste procedimento é testar velocidade – porque como dito anteriormente o foco deste nosso componente é
“disparar” velocidade trazendo agilidade em nossos processamentos em uma DBGrid – daí a criação do nosso
“componente-foguete”. Resultado dos testes:
Componente TClientDataSet THVDataSet
Carregar e ler um milhão de
registros
Tempo: 5 minutos e 17 segundos 5 segundos
Figura 01 – Mensagem de retorno da confirmação do teste – componente TClientDataSet.
Figura 02 – Mensagem de retorno da confirmação do teste – componente THVDataSet.
Figura 03 – Ilustração dos componentes carregados – THVDataSet e TClientDataSet.
Se perceber bem, todos ambos os componentes carregaram com sucesso. O problema não é esse – eles funcionam
e bem – o problema é O TEMPO que é levado para tal – pois enquanto um levará de cinco a seis minutos, o nosso
componente levará de cinco a seis segundos! E isso é ótimo quando o que se quer é carregar milhares e milhões de
registros de uma vez. Tudo funciona muito bem, claro, mas há limitações quanto a memória disponível – não vamos
carregar bilhões de itens porque obviamente estourará a memória – precisamos ser sensatos porque nem tudo são flores
– se está inserindo itens dinamicamente em um objeto de TDBGrid, leve-se em conta que esses dados consomem
memória além do THVDataSet – e do TDBGrid também.
Claro que não é a forma mais rápida de todas – existem alternativas melhores – por exemplo – estamos lendo
para um TDBGrid – se quiséssemos rapidez de verdade – tiraríamos o TDBGrid e leríamos a partir de um classe
descendente de um TList – aí sim carregaríamos este conjunto de um milhão de registros em milésimos de segundo! E
isso é ótimo. Mas o que queremos neste foco é abordar a velocidade deste processamento aliado também ao
componente TDBGrid (coisa que um TList não tem suporte) e por isso a velocidade “cai” um pouco (mas nem tanto,
como já vimos acima). O resultado é que tudo funciona as mil maravilhas. Claro que o quesito memória conta e muito –
para os nossos testes foi testado processador 2.50 GHz com 4GB de RAM. Recomendável sempre a partir destas
configurações para mais – a fim de se obter resultados ainda mais rápidos.
Vamos inserir abaixo um pequeno trecho do fonte responsável para a implementação desta nossa função de teste
– foi utilizado uma classe chamada “TStopWatch” para auxiliar na precisão da medição dos nossos resultados (algo como
um cronômetro bem preciso) – utilizando das funções da API Kernel32 do Windows – QueryPerformanceFrequency e
QueryPerformanceCounter para fazer a medição do tempo com uma resposta em milésimos de segundos, muito eficiente
e robusto suas respostas de testes.
const
cUserFieldName = 'Cd_User';
const
cDefaultStopWatchInitialText = '0 elapsed milliseconds.';
cStopWatchSuccess = 'Tested Component: %s.'#13#10'%s elapsed
milliseconds.';
implementation
uses StopWatch;
procedure TForm1.Button2Click(Sender: TObject);
var
i, counter: integer;
sw : TStopWatch;
begin
HVDataSet1.Active := True;
HVDataSet1.DeleteAll;
counter := StrToIntDef(Edit1.Text, 10);
stStopWatch.Caption := cDefaultStopWatchInitialText;
HVDataSet1.DisableControls;
sw := TStopWatch.Create;
try
pnlLoading.Visible := True;
Screen.Cursor := crSQLWait;
try
sw.Start;
for i := 0 to counter do
begin
with HVDataSet1 do
begin
Append;
FieldByName(cUserFieldName).AsInteger := i;
Post;
end;
Application.ProcessMessages;
end;
Finally
pnlLoading.Visible := False;
Screen.Cursor := crDefault;
HVDataSet1.EnableControls;
sw.Stop;
stStopWatch.Caption := 'Elapsed ' +
FormatMillisecondsToDateTime(sw.ElapsedMilliseconds);
end;
finally
FreeAndNil(sw);
end;
HVDataSet1.First;
Application.MessageBox(PChar(Format(cStopWatchSuccess, ['THVDataSet',
stStopWatch.Caption])), 'Information', MB_OK + MB_ICONINFORMATION);
end;
Utilizando o componente na prática
Agora vamos demonstrar o componente na prática, através de um aplicativo de teste para inserir vários registros
e medindo o tempo gasto – o propósito deste programa é carregar um milhão de registros e ver quanto tempo ele vai
levar pra fazer isso – serão três colunas criadas dinamicamente pelo THVDataSet (uma do tipo inteiro, outra do tipo
string e outra do tipo time) e com uma função semelhante como visto anteriormente vamos implementar esta
funcionalidade aqui. Alguns fatores novos interessantes foram incluídos para facilitar e agilizar a performance dos nossos
testes; vamos portanto elencar eles agora:
1. Inclusão do framework FastMM4 (Fast Memory Manager 4.991);
2. Inclusão do framework Synopse (função Int64ToUTF8);
3. Inclusão da thread TProgressThread (criada por mim);
4. Inclusão da classe opensource TStopWatch.
Todas essas classes mencionadas acima são de suma importância para agilizar os testes através de um melhor
gerenciamento de memória, principalmente pelo framework FastMM4. Nossos testes foram realizados em Delphi 7.
Figuras 04, 05 – Demonstração do aplicativo de teste do componente THVDataSet.
Para este teste foi levado o tempo médio de 36 segundos para um milhão de registros criados em três colunas
dinamicamente – isso tudo em memória.
Conclusão
Vimos uma implementação em memória muito prática e veloz, através de um componente chamado de
THVDataSet. Com ele pudemos ver a sua eficiência superior ao componente tradicional do Delphi (TClientDataSet) onde
o foco é a velocidade em milhares e milhões de registros em pouquíssimo tempo. É claro que não se tem a intenção de
substituir o TClientDataSet no seu emprego normal; nosso THVDataSet é um descendente de TMemoryStream e não tem
as características de um TClientDataSet embutidas nele (índices, agrupamentos, etc) – mas para armazenamento
temporário de grandes quantidades de dados e suas manipulações via componentes Data-Aware poderão com certeza
contar com ele! Um grande abraço e até a próxima!
Utilizem com prazer este grande componente “armazenador de velocidade”!!!
Sobre o Autor
Hamden Vogel Consultor TheClub
E-mail: [email protected]