Upload Assíncrono via Ajax com Rails

Você já precisou fazer Upload de arquivos no Rails de forma assíncrona (sem precisar dar reload na página)?

Nesse Tutorial que o Daniel Moreto (aluno do Bootcamp Super Full Stack \o/) fez para o OneBitCode, nós vamos aprender o passo a passo para implementar essa feature no seu software sem precisar usar plugins Jquery externos de maneira limpa e rápida.

*Obrigado Daniel pelo conteúdo incrível 🙂

Objetivo

Fazer Upload assíncrono de imagens no Rails

Ingredientes

  • Ruby
  • Rails On Rails

 

Introdução:

E aí galeera!

Sou o Daniel Moreto e hoje eu vou mostrar pra vocês como fazer um upload de arquivo via Ajax no Ruby On Rails.
Lembrando, esta é apenas uma forma de fazer isso. Bem rápida e sem plugins jquery adicionais.
Bem, partiu código então!

Gerando nosso Projeto

1 – Primeiro, rode no console para gerar o projeto de exemplo:

2 – Se estiverem utilizando Rails 5, precisaremos colocar no Gemfile a gem jquery-rails

3 – Agora para instalar a nova gem rode no console:

4 – Depois disso, remover o rails_ujs do application.js e colocar o jquery e o jquery_ujs. O jquery_ujs vamos precisar pra nos dar um help quando formos fazer uma chamada Ajax e não termos problema com CSRF Token Authenticity

 

Criando nosso Controller

Bem, agora vamos aos códigos que possibilitam este nosso upload. Vou começar explicando a estrutura dos arquivos backend pra vocês.

1 – Rode no console para criar um novo controller

2 – Coloque no controller gerado:

Explicação:

Os meus arquivos serão salvos na pasta “public/sample_files/”, então na action “index” do meu controller eu estou simplesmente listando todos os arquivos com os respectivos nome (:name) e link e renderizo o meu template :index.

Na action #new eu apenas renderizo o template :new (Onde o usuário vai poder subir o arquivo).

Na action #create eu precisei criar um recurso chamado FileBuilder que vou explicar em seguida. Nesta action #create eu recebo como parâmetro uma chave “files” e nela vem as listas de arquivos que enviei a partir do front-end com o nome e conteúdo do arquivo. Então eu percorro a lista e pego cada arquivo para ser reconstruído usando o FileBuilder a partir do conteúdo. Como o conteúdo chegou aqui eu vou explicar daqui a pouco, antes eu preciso mostrar o FileBuilder, que está nos ajudando a construir nosso arquivo.

3 – Atualize o seu config/routes.rb colocando:

 

Criando nossa Lib

1 – Em lib/ crie um arquivo chamado file_builder.rb e coloque nele:

Explicação:

O FileBuilder é uma lib (diretório lib/ do Rails) que eu criei que vai receber o path do arquivo que eu quero construir, o nome e conteúdo. Se você reparar, na linha 20 do meu AsyncUploadsController eu instancio o FileBuilder e na linha 21 eu chamo um método file_builder.from_base_64 para criar o arquivo. Mas, como eu faço isso?

Quando eu vou enviar alguma coisa via Ajax, não é possível passar nada além de texto, então para enviar um arquivo, seja ela uma imagem, .doc, .xlsx ou qualquer outra coisa, eu preciso pegar o conteúdo do meu arquivo, que é binário, e transformar numa cadeia de caracteres (texto). Pra fazer isso, nós recorremos à codificação Base64, que é feita no JS.

Apenas para esclarecer o Base64 é um método de codificação de dados (binário) em texto para transferência pela Internet.

Neste método from_base_64 eu faço justamente a decodificação desta sequência Base64 (o oposto do que o JS faz), que volta a ser binário e passo como parâmetro do método generate_file que visa criar o arquivo com o conteúdo em binário.

Mas e como eu faço essa sopa de letrinha chegar até o servidor? Bem, vamos aos arquivos front-end agora.

 

Criando nossas Views

1 – Para vermos a lista de arquivos, coloque na view “app/views/async_uploads/index”

 

 

2 – Para poder fazer o upload do arquivo no front end coloque na view “app/views/async_uploads/new”:

 

 

 

Criando nosso JS

Nós precisamos de um arquivo JS para capturar o evento do click no botão send e realizar o upload via ajax do nosso arquivo.

1 – Crie um arquivo chamado (“app/assets/javascripts/async_upload.js”) e coloque nele:

Explicação:

Repare o seguinte: no final no arquivo há uma função chamada readfile que aceita um parâmetro file e um outro callback_exec. Bem, é nesta função que está o segredo da nossa codificação Base64.

Eu utilizo um recurso nativo do JS chamado FileReader. Primeiramente, na linha 31, eu instancio esta classe. Depois eu pego o objeto e atribuo ao onload dele uma função que eu criei. Esta função vai chamar o meu callback_exec internamente passado como parâmetro e vai enviar como argumento para ela o e.target.result que já é o conteúdo do arquivo que eu enviei como argumento file com a codificação Base64.

Mas, onde eu chamo este onload? Ele é um callback (outro) da função que eu chamo logo abaixo, a reader.readAsDataURL e este callback onload recebe como argumento o mesmo file que envio para a readAsDataURL.

Mas de onde vem este callback_exec? Por que ele é chamado? Já chegaremos lá!

Vamos ao topo do arquivo. Primeiro eu crio uma lista files_to_upload vazia. Depois eu atribuo ao meu campo input file um event change. Ou seja, toda vez que alguma coisa for anexada lá, esse evento vai disparar.

Meu próximo passo é pegar os arquivos que estão anexados neste campo, que faço na linha 6. Depois em cada arquivo que foi anexado nesta lista, eu chamo a minha função readfile que está lá embaixo e passo como um dos argumentos uma função que vai acrescentar na minha lista files_to_upload um objeto contendo o nome e o conteúdo do arquivo.

Mas por que criar um callback dentro do change pra isso? Justamente porque o FileReader funciona por meio de chamada callback. Apenas quando o conteúdo do arquivo é carregado para o objeto que a função onload do FileReader será chamada. A saída para isso é colocar o onload do FileReader para executar uma função minha, preenchendo a minha lista =).

Nas linhas seguintes eu apenas atribuo um evento click ao meu botão de enviar e chamo a função jquery $.post para fazer um post assíncrono enviando a minha variável files_to_upload, que chamará a minha action :create do meu AsyncUploadsController e o resto vocês já sabem. =D

Uma observação que eu acho importante é a seguinte: quando a gente utiliza este métodos, o nosso log de aplicação vai ficar bem poluído, pois como tudo é texto, o Rails vai colocar todo o conteúdo Base64 do arquivo no log. Para deixar ele mais limpo, podemos adicionar um filtro no parâmetro no config/application.rb.

Dessa forma, nossos logs virão com um [FILTERED] on lugar de todo o conteúdo de caracteres Base64.

Upload de multiplos Files

Outra coisa legal, é que o campo de arquivos (async_upload_file) que criamos aceita apenas um arquivo por vez, mas nossa estrutura já vem preparada para receber vários arquivos de uma vez, se for necessário. Então, para poder enviar mais de um arquivo de uma vez, basta colocar nosso campo como multiple:

Assim podemos selecionar vários arquivos e enviar =)



12 formas de vencer o bloqueio criativo e escrever textos memoráveis (e 6 dicas extras)

Não perca nenhum conteúdo

Receba nosso resumo semanal com os novos posts, cursos, talks e vagas o/



Conclusão

Bem, galera, é isso.

Se tiverem alguma dúvida, eu tenho um repositório com o código de exemplo no link: https://github.com/dfmoreto/async_upload_ajax_rails

Sintam-se à vontade para enviar perguntas nos comentários abaixo ou até mesmo me contactar por redes sociais.

Github: https://github.com/dfmoreto
Linkedin: https://www.linkedin.com/in/dfmoreto/
Facebook: https://www.facebook.com/daniel.moreto.3

 

Até Breve =)

Daniel Moreto


Você é novo por aqui?

Primeira vez no OneBitCode? Curtiu esse conteúdo?
O OneBitCode tem muito mais para você!

O OneBitCode trás conteúdos de qualidade e em português sobre programação com foco em Ruby on Rails e outras tecnologias como Angular, Ionic, React, desenvolvimento de Chatbots e etc.

Se você deseja aprender mais, de uma forma natural e dentro de uma comunidade ativa, visite nosso Facebook e nosso Twitter, veja os screencasts e talks no Youtube, alguns acontecimentos no gay dating kansas city, ouça os top gay men e faça parte de nossa Newsletter.

Além disso, também estamos com alguns e-Books muito interessantes para quem deseja se aprimorar como programador e também como freelancer (os e-Books são gratuitos!):

Espero que curta nossos conteúdos e sempre que precisar de ajuda com os tutoriais, fala com a gente!
Seja por
Facebook ou e-mail, estamos aqui para você 🙂

Bem-vindo à família OneBitCode \o/

0 0 votes
Article Rating
janeiro 17, 2020
Subscribe
Notify of
guest
3 Comentários
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Francisco
Francisco
5 anos atrás

Extremamente interessante e funcional. Mas como conseguiria aplicar este caso em um cadastro de clientes por exemplo? Ou seja, criar a pasta no public específica por id de cliente?

Jonatas Moreira
4 anos atrás

Parabéns pelo post Daniel, muito bem explicado e útil!
Rodou legal aqui na minha máquina quando enviei uma imagem .jpg, mas quando testei uma outra extensão da erro. (undefined method `[]’ for nil:NilClass).
E o engraçado é que mesmo assim a imagem é enviada, mas depois disso ele fica em um looping do erro, mesmo parando o servidor.
A forma que resolveu, não a ideal, foi remover a imagem do diretório.
Onde é possível fazer a definição de extensão aceitável pra upload das imagens?
Você obteve esse erro também?
Abraços

Feito com s2 por OneBitCode

3
0
Would love your thoughts, please comment.x
()
x
%d blogueiros gostam disto: