News & Events
Tutorial: Download e Upload de arquivos binários em Base64 com Mobile e DataSnap 1/2
- 29 de setembro de 2016
- Posted by: Adriano Santos
- Category: Notícias
Frequentemente recebo mensagens de meus leitores solicitando dicas de como fazer download e upload de arquivos binários em servidores DataSnap usando Base64. A utilidade desse tipo de recurso é diversa. Imagine, por exemplo, que estamos desenvolvendo uma app de catálogo de produtos onde nosso banco de dados está na nuvem e no server existe uma pasta onde todas as imagens dos produtos estão armazenadas. Cada vez que o usuário toca em um produto, exibimos os dados dele e a foto (ou fotos) desse item. Nós vimos aqui mesmo no blog na postagem Tutorial: Salvar foto no banco SQLite com Android e iOS algo semelhante, mas nesse caso trazendo a foto do banco de dados SQLServer vinda pela classe FDJSONDataSets do servidor DataSnap e salvando no banco SQLite local. Entretanto não estamos usando Base64. Nesse método podemos baixar (ou upar) a imagem (ou qualquer outro arquivo) por pedaços, assim podemos até incrementar o app mostrando uma barra de progresso, excelente ideia. Se você usa Instagram, certamente prestou atenção no fato dele trazer as fotos rapidamente na tela e com um progress em cima da imagem. Vejamos como fazer algo parecido em Delphi.
Download e Upload de arquivos binários em Base64
O que vamos fazer aqui é relativamente simples e pode ser usado para qualquer tipo de arquivo que se queira baixar ou enviar ao servidor DataSnap. Basicamente vamos criar dois ServerMethods no servidor, um para baixar e outro para fazer o upload. Simularemos o download e upload para uma pasta específica no servidor, mas esse recurso poderia ser usado para receber a imagem no servidor e guarda-la no banco de dados se desejar, e vice-versa.
Criando o servidor DataSnap
Não entrarei em detalhes quanto a criação do servidor DataSnap, se tiver dúvidas acompanhe nosso tutorial Criando meu app step-by-step (Aula VI) ou ainda no YouTube o Curso: Criando aplicativos móveis com Delphi (Aula 09) sobre o assunto. No servidor DataSnap uma nova Unit será criada, nosso ServerMethods. Nós criaremos dois métodos, vamos começar primeiro com a function que fará o download da imagem do servidor para o aplicativo móvel. Então comece declarando uma nova function na seção Public como mostrado abaixo:
function DownloadFile(const AFile: String; out Size: Int64): TStream;
O método acima é bem simples de entender. Criamos um parâmetro que receberá o nome do arquivo que o usuário deseja baixar, apenas para facilitar o exemplo, mas poderia ser qualquer informação, como o ID de um registro na tabela por exemplo onde faríamos um SELECT no banco para descobrir qual imagem associada, enfim. O segundo parâmetro será usado para informar à aplicação CLIENT qual o tamanho do arquivo, assim podemos atualizar uma ProgressBar enquanto o arquivo é baixado. E o Result do nosso método será do tipo TStream, ou seja, vamos criar o arquivo em memória e repassa-lo para a aplicação que o requisitou. Pressione agora CTRL + SHIFT + C para que o Delphi crie o escopo da function. Nele digite a codificação abaixo:
function TMetodosGerais.DownloadFile(const AArquivo: String; out Size: Int64): TStream; var ImagePath: String; begin ImagePath := ExtractFilePath('..\..\..\img\'); Result := TFileStream.Create(ImagePath + AFile, fmOpenRead or fmShareDenyNone); Size := Result.Size; Result.Position := 0; end;
Muito bem, vamos às explicações. Primeiro declaramos uma variável que será responsável por receber o caminho da imagens, a pasta onde ela se encontram. É evidente que em uma app real esse caminho pode vir do banco de dados ou de qualquer outro local à sua escolha. Vamos simular que todas as imagens ficam armazenadas na pasta “img” no diretório de instalação de nosso servidor DataSnap. Por isso na primeira linha de nosso método atualizamos a variável com esse caminho.
Na sequência, instanciamos o Result chamando o método Create da classe TFileStream onde passamos o caminho concatenado ao nome da imagem recebido no parâmetro AFile. O segundo parâmetro é usado para determinar o modo de abertura da imagem, haja vista que ela deverá ser lida e carregada em memória. Por fim, passamos o tamanho da imagem à variável de saída [out] para ser usado na aplicação client e posicionamos o result em 0 (zero).
Nosso trabalho no server, ao menos nesse momento, está encerrado. Vamos até a aplicação cliente para codificar a chamada. Copie para a pasta “img” ao todo quatro imagens e renomeie-as para “001.jpg”, “002.jpg”, “003.jpg” e “004.jpg”.
Aplicação Cliente
Novamente não entrarei em detalhes sobre como vincular o aplicativo cliente ao servidor DataSnap. Tomei por base que você já sabe como fazer isso ou leu algum de meus tutoriais. No formulário principal de nosso aplicativo móvel, crie um formulário semelhante ao mostrado na imagem seguinte. Nesse formulário inseri uma ToolBar com três botões dentro dela. Em seguida adicionei um ProgressBar e coloquei seu alinhamento como sendo “Top” para que fique grudado embaixo da ToolBar. Para Android ele ficará bem fininho quase não dando para perceber. Na sequência, também grudado ao topo, inseri um Label que receberá o status de download, ou seja, algo como “Baixando 10Kb de 100Kb”, e assim sucessivamente. Também adicionei um ComboBox e em sua propriedade Items inseri os nomes das imagens que poderão ser baixadas. Pra fechar o visual coloquei um componente TImage para receber e exibir a imagem recebida pelo método DownloadFile do servidor.
O próximo passo é codificar o evento do botão Download. Por isso, acesse seu evento OnCllick e digite o código da listagem abaixo:
procedure TfrmPrincipal.Button1Click(Sender: TObject); var RestStream : TStream; Mem : TMemoryStream; Buffer : PByte; BufSize : Integer; BytesRead : Integer; Size : Int64; Filename : String; Destino : String; begin BufSize := 1024; try Mem := TMemoryStream.Create; GetMem(Buffer, BufSize); try FileName := ComboBox1.Items[ComboBox1.ItemIndex]; //Download do arquivo do servidor para o cliente RestStream := CM.MetodosGeraisClient.DownloadFile(FileName, Size); RestStream.Position := 0; if (Size <> 0) then begin pgbProgresso.Max := Size; pgbProgresso.Value := 0; repeat BytesRead := RestStream.Read(Pointer(Buffer)^, BufSize); if (BytesRead > 0) then Mem.WriteBuffer(Pointer(Buffer)^, BytesRead); lblStatus.Text := Format('Baixando %s de %s bytes', [IntToStr(Mem.Size), IntToStr(Size)]); pgbProgresso.Value := Mem.Size; Application.ProcessMessages; until (BytesRead < BufSize); {$IFDEF MSWINDOWS} Destino := ExtractFilePath(ParamStr(0)) + FileName; {$ELSE} Destino := System.IOUtils.TPath.Combine(System.IOUtils.TPath.GetDocumentsPath, FileName); {$ENDIF} Mem.SaveToFile(Destino); if ExtractFileExt(Destino) = '.jpg' then Image1.Bitmap.LoadFromFile(Destino); if (Size <> Mem.Size) then raise Exception.Create( 'Erro ao baixar...' ); end else lblStatus.Text := EmptyStr; finally FreeMem(Buffer, BufSize); FreeAndNIl(Mem); lblStatus.Text := EmptyStr; pgbProgresso.Max := 0; pgbProgresso.Value := 0; end; except on E: Exception do lblStatus.Text := PChar( E.ClassName + ': ' + E.Message ); end; end;
O código é extenso, eu sei, mas não se assuste, porque é simples de entender. Vamos lá. A primeira coisa é que declaramos uma série de variáveis para controlar o download do arquivo, não vou explicar todas, apenas as mais importantes. A primeira é a variável RestStream, responsável por receber o arquivo em memória vindo do servidor. Percebemos que ela é do mesmo tipo de retorno do método DownloadFile, ou seja, TStream. Em seguida declaramos Size para receber o tamanho do arquivo e FileName para saber qual a imagem que o usuário quer baixar do servidor. Essa variável está associada ao item de ComboBox selecionado. Target é a variável que receberá o destino da imagem, nesse caso a pasta Documents no mobile, no device.
O início do procedimento faz o trabalho de instanciar as variáveis que serão usadas para controlar a memória e o download do arquivo. Os pontos mais importantes aqui são o momento que fazemos referência ao servidor, descrito na linha abaixo:
RestStream := CM.MetodosGeraisClient.DownloadFile(FileName, Size);
Essa linha é responsável por chamar o método de download da imagem no servidor DataSnap. Perceba que passamos o nome da imagem em FileName e a variável que receberá o tamanho do arquivo, Size. Em seguida verificamos, por uma condicional, se a variável Size é maior que 0 (zero), indicando que algum arquivo foi “capturado” do lado server. Se isso for positivo, então começamos a brincadeira de baixar o arquivo.
repeat BytesRead := RestStream.Read(Pointer(Buffer)^, BufSize); if (BytesRead > 0) then Mem.WriteBuffer(Pointer(Buffer)^, BytesRead); lblStatus.Text := Format('Baixando %s de %s bytes', [IntToStr(Mem.Size), IntToStr(Size)]); pgbProgresso.Value := Mem.Size; Application.ProcessMessages; until (BytesRead < BufSize);
Perceba, estamos fazendo um laço repeat…until. Em outras palavras, repita até que a condicional atenda aos critérios. Nesse caso estamos verificando se os bytes lidos (BytesRead) é menor que os bytes já recebidos (BufSize). Esse é um momento crucial, pois dependendo do tamanho da imagem, pode durar algum tempo e a aplicação travar, por isso é importante informar ao usuário o que está acontecendo. Por esse motivo atualizamos o ProgressBar e o Label com as informações necessárias.
Quando nosso loop terminar, é hora de atualizamos a janela de nosso aplicativo. Mas antes disso, precisamos salvar o arquivo na pasta Documents do aplicativo móvel ou em uma pasta de nosso gosto caso esteja rodando no Windows. Verifique no trecho abaixo que estamos usando a diretiva de compilação
{$IFDEF MSWINDOWS}
para saber se estamos rodando o app no Windows ou Mobile, iOS e Android.
{$IFDEF MSWINDOWS} Target := ExtractFilePath(ParamStr(0)) + FileName; {$ELSE} Target := System.IOUtils.TPath.Combine(System.IOUtils.TPath.GetDocumentsPath, FileName); {$ENDIF} Mem.SaveToFile(Target); if ExtractFileExt(Target) = '.jpg' then Image1.Bitmap.LoadFromFile(Target); if (Size <> Mem.Size) then raise Exception.Create( 'Erro ao baixar...' );
O que estamos fazendo aqui é bem simples. Observe que atualizamos a variável Target com o caminho onde vamos salvar a imagem. Em seguida chamamos o método SaveToFile da variável Mem que possui o arquivo carregado em memória. Salvamos esse arquivo para o diretório capturado em Target. Por fim, carregamos o arquivo baixado em Documents para a propriedade Bitmap do componente Image1 usando o método padrão, LoadFromFile. O restante do método são os tratamentos de exceções caso algum erro ocorra.
Conclusão
Se você executou todos os passos corretamente até aqui, já pode executar seus aplicativos servidor e mobile e efetuar o teste. Perdeberá que o arquivo será baixado para o dispositivo móvel e mostrado no Image1. Para não estender demais o tutorial, vou parar por aqui. Assim você terá tempo suficiente para entender e reproduzir o exemplo. Nos encontramos na parte 2 de nosso tutorial onde veremos o processo inverso, inclusive tirando uma foto na hora e “subindo” para o servidor DataSnap.
Atenção: Para estimular os estudos, não fornecerei o código-fonte do aplicativo.
#GoToDelphi
Author: Adriano Santos
1 comentário
Comments are closed.
[…] nosso último post entitulado Tutorial: Download e Upload de arquivos binários em Base64 com Mobile e DataSnap 1/2 nós vimos como fazer download de fotos de um servidor DataSnap para a aplicação cliente, […]