Simulando chamadas Ajax para upload de arquivos
Esse artigo vai mostrar como fazer upload de imagens e arquivos de forma assíncrona. A solução apresentada não será nada que não venha sendo usado a pelo menos 4 anos, mas acho que vale a explicação. Na primeira parte, a explicação será mais ampla, independente de linguagem. Em seguida, trataremos só de Rails, usando o plugin responds_to_parent.
Você usa o Gmail? Se usa já deve ter visto a forma assíncrona de envio de arquivos. Nada de mandar fazer o upload. Selecione o arquivo e pronto, o upload comece a ser feito, sendo depois substituído por um link. Acompanhe nas imagens abaixo:
Aparentemente nada de mais, afinal, Ajax já está há tempos na nossa vida. Correto? Bom, infelizmente, incorreto! Isso porque XMLHttpRequest não faz envio de arquivos. Um dos motivos vem do fato do Javascript ser executado em um sandbox isolado dos arquivos do seu computador por questões de segurança.
Para contornar essa restrição, existem, basicamente, dois modelos:
1) o primeiro é usando o Flash (se gosta disso, veja no final o link sobre o swfupload). Essa forma é prática, mas eu, pessoalmente, sou totalmente contra. Acho o Flash algo muito intrusivo e inútil em aplicações mobile. E agora, com o HTML5 e as tags de vídeo e áudio incorporadas ao browser, para mim ele tende a morrer. Morrer é forte, mas seguramente perder muito espaço;
2) A segunda forma é a usada no Gmail e em várias outras aplicações espalhadas na internet. É a submissão do formulário para um target que é na verdade, um iframe oculto na própria página. É sobre isso esse artigo.
Para isso, mudamos nosso formulário normal para submeter para um target.
<form target=’frame_upload’ enctype=’multipart/form-data’ id=’form_upload’ method=’post’ action=’/image’>
<input id=’photo’ name=’photo’ size=’30′ type=’file’>
<input value=’Upload »’ type=’submit’>
</form>
<iframe id=’frame_upload’ style=’width:1px;height:1px;border:0px’></iframe>
UPDATE: antes eu havia escrito que era para usar style display:none. Porém, esse artigo me fez mudar de idéia ao falar que alguns browsers podem ignorar o iframe e enviar o arquivo para uma nova página por conta do display:none.
A página será submetida como uma ação post normal. A diferença é que o resultado atualizará apenas o iframe que aqui chamamos de frame_upload. E aí vem o golpe de mestre, o retorno deverá ser um html contendo um script. Esse script deve conter uma chamada para avaliar a resposta em um window.parent. Window.parent nesse caso será a página que contém o iframe. Fazendo, com isso, que a chamada se pareça com uma chamada Ajax normal.
Como exemplo, vejamos como é a resposta que o plugin responds_to_parent gera no Rails:
<html><body>
<script type='text/javascript' charset='utf-8'>
var loc = document.location;
with(window.parent) { setTimeout(function() { window.eval(’#{SCRIPT_A_SER_EXECUTADO}’); loc.replace(’about:blank’); }, 1) }
</script>
</body>
</html>
Onde SCRIPT_A_SER_EXECUTADO é o que vai, no final das contas alterar a página pai. Simples e genial.
Fazendo o upload usando o Rails e o plugin responds_to_parent
NOTA: Para que a explicação não deixe dúvidas, criei uma aplicação de exemplo no GitHub, é só ver no link abaixo:
http://github.com/riopro/iframe/tree/master
Como implementar em RoR? Para isso, vamos usar o plugin responds_to_parent. Ele é simples e não altera a sua resposta rjs normal. O primeiro passo é baixar o plugin em:
http://code.google.com/p/responds-to-parent/downloads/list
e descompactá-lo na pasta vendor/plugins da sua aplicação. Na resposta da sua action, você simplesmente aninha a resposta dentro de uma chamada responds_to_parent, dessa forma:
responds_to_parent do
render :update do |page|
page.replace_html(’image_upload’, :partial => ‘image_upload’, :locals => {:image => @image})
end
end
Nesse caso, a minha resposta vai substituir o div com id image_upload com a resposta. Sendo que o div image_upload deve existir na página que contém o iframe. Espero que seja útil essa dica.
Links úteis complementares:
http://github.com/otaviofcs/iframe/tree/master
http://kpumuk.info/ruby-on-rails/in-place-file-upload-with-ruby-on-rails/
http://sean.treadway.info/responds-to-parent/
http://khamsouk…ajax-file-uploads-in-rails-…and-responds_to_parent





[…] Uma aplicação de exemplo foi criada usando Rails e o plugin livre responds_to_parent, mas a explicação inicial vale para qualquer linguagem.” [referência: blog.riopro.com.br] […]