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:

gmail_upload.png

gmail upload feito

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

http://malsup.com/jquery/form/

http://swfupload.org/

Informações e Links

Junte-se comentando, lendo o que os outros dizem ou colocando um link a partir do seu blog.


Outros Artigos
Enviando imagens para S3 assincronamente em Rails
Tornando o Firefox 3.5 o seu browser padrão no Ubuntu 9.04

Comente

Tire um tempo para comentar e nos dizer o que você acha. Alguns códigos HTML são permitidos para formatação.

Comentários dos Leitores

[…] 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] […]

Excelente artigo camarada. Simples e mostra tudo que se precisa.

obrigado. Espero que tenha sido útil…

[…] Simulando chamadas Ajax para upload de arquivos Hits para esta publicação: 1 […]

Interessante.

Edluise Costa
http://www.ecadti.com.br/