In-place file upload with Ruby on Rails

Posted by Dmytro Shteflyuk on under Ruby & Rails

My friends often asked me how to upload file using AJAX, and usually they got answer “in no way”. Correct answer, but what if I need to upload file without full page reloading? And, of course, I want to use RJS in this case. Here I’ll explain what to do to get effect very similar to AJAX file upload (btw, Gmail uses this technique).

First of all, do you know, that form element has attribute target? If you specify it your form will be submitted into frame with name entered in target attribute. Of course, this frame can be iframe, and it can be hidden! Look at the following chunk of HTML code:

1
2
3
4
5
<form target="upload_frame" action="/test/upload_action" id="upload_form" method="post" enctype="multipart/form-data">
  <input type="file" name="uploaded_file" /><br />
  <input type="submit" />
</form>
<iframe id="upload_frame" name="upload_frame" style="display: none"></iframe>

When you click the “Submit” button, form will be submitted to hidden iframe and controller’s action will be called. But resulting RJS will be returned into the hidden frame! We need to get full respond and execute it in context of whole page (parent window for our frame). There is one interesting technique exists: http://developer.apple.com/internet/webcontent/iframe.html, and it’s implementation as Ruby on Rails plugin here. Example of using:

1
2
3
4
5
6
7
8
9
10
11
class TestController < ActionController::Base
  def upload_action
    # Do stuff with params[:uploaded_file]

    responds_to_parent do
      render :update do |page|
        page.replace_html 'upload_form', :partial => 'upload_form'
      end
    end
  end
end

It’s simple, right?

32 Responses to this entry

Subscribe to comments with RSS

Gomes
said on November 6th, 2006 at 12:04 · Permalink

Классная статья, спору нет. Созрел только небольшой вопросик как отследить загрузился ли файл на сервер, ведь существует куча преград при загрузке (размер файла превышает разрешенный и т.п.). Отдавать с сервера страницу с JavaScript, который бы дернул функцию на странице как то не красиво (да и не всегда сработает), а как узнать кроссбраузерно что пришло в теле страницы с сервера для меня загадка, если не сложно прошу расширения статьи.

said on November 20th, 2006 at 17:04 · Permalink

Попробуем применить на практике. Еще было бы интересно состыковать эту штуку с плагином upload_progress

said on November 20th, 2006 at 17:10 · Permalink

Ага, были такие мысли, займусь на досуге.

said on November 20th, 2006 at 21:30 · Permalink

[…] There is no way to upload files using AJAX. How Gmail does it then? Well, the answer is using <iframe>. Here is a post that describe this technique in detail. Infact we at Vinayak Solutions are using this technique from months, obviously inspired by Gmail. ;-) […]

Anton Kirsanov
said on November 23rd, 2006 at 10:35 · Permalink

Добавлю только, что для проверки статуса достаточно запустить интервалом в секунду калбэк функцию (я о javascript говорю), которая будет смотреть readyState у окна в скрытом фрейме.

said on November 23rd, 2006 at 18:44 · Permalink

Спасибо, погляжу

Maximark
said on November 23rd, 2006 at 18:58 · Permalink

По поводу загрузки файлов без обновления, напоминаю прекрасную библиотеку dklab.ru
http://dklab.ru/lib/JsHttpRequest/

Версия 4.0

я уже писал как то про неё, но эффекта было ноль. И вот… получите и спокойно пользуйтесь. На сегодня я думаю это самое лучшее реализация технологии AJAX. И тем паче загрузки файлов без перезагрузки страницы !!!
Это не реклама… я сам давно пользуюсь и не я её писал.

said on December 1st, 2006 at 09:36 · Permalink

[…] There is no way to upload files using AJAX. How Gmail does it then? Well, the answer is using <iframe>. Here is a post that describe this technique in detail. Infact we are using this technique from months, obviously inspired by Gmail. […]

said on January 4th, 2007 at 02:46 · Permalink

great article! its a tricky problem but your solution works like a charm ;-)

Rails Developer
said on January 29th, 2007 at 21:00 · Permalink

Thanks for the help but from the Apple site, %{style=”display: none”} doesn’t work on the iframe but if you set it to %{style=”height:0;width:0;border:0;”} then it will work.

said on February 12th, 2007 at 23:58 · Permalink

Hey, nice article.. I actually wrote a plugin that does this. I’m using it in a couple places, but had to write it so quickly that I wasn’t able to adequately test it on all platforms. You can check it out here: http://rubyforge.org/projects/remote-upload/

Please tell me what you think

John
said on February 16th, 2007 at 19:20 · Permalink

I’m using your code almost exactly and most of it seems to be working, but when i try to do something with the uploaded_file all it has is the filename, not the actual file data. Any ideas?

said on February 22nd, 2007 at 01:20 · Permalink

Jeff, great news! Will check your plugin in short time. Thanks!

said on March 13th, 2007 at 19:17 · Permalink

Great article! Thanks! I’ve combined your solution with attachment_fu plugin and result is awesome! Thanks!

said on April 23rd, 2007 at 11:37 · Permalink

Спасибо а идею реализации. Думаю что этой техникой можно пользотаться и в других случаях.

jeffguroo
said on December 11th, 2007 at 00:22 · Permalink

Seems as though the problem with the lack of data being transfered is a problem with Firefox. When I use ff the files are always empty, however when I use Exploder, the data is carried over.

jeffguroo
said on December 11th, 2007 at 00:24 · Permalink

this solution only seems to work on txt files. Formatting for PDFs and Word Docs were hosed.

jeffguroo
said on December 12th, 2007 at 00:26 · Permalink

Alright, that was because I was trying to do it with remote_form instead of a form_for. Only the text was making it through. BTW what was the name of the plugin that was used for returning js to the iframe? That link is dead.

jeffguroo
said on December 12th, 2007 at 02:46 · Permalink

The above mentioned plugin is: respondes_to_parent

said on December 12th, 2007 at 06:35 · Permalink

jeffguroo, you right
It’s impossible to upload file using AJAX’s XMLHttpRequest. So I have written this post to show how to do it using iframe.

Thanks for comment about broken link, I have fixed it.

jeffguroo
said on December 12th, 2007 at 07:34 · Permalink

after beeting my head against this all day, I have found that doing a simple

1
render :partial => "successful_attachment"

also works quite well in Rails v1.2.3 and the acts_as_attachment plugin. Thanks for the file upload/AJAX workaround… a lifesaver.

said on January 7th, 2008 at 04:08 · Permalink

All you really need is the first part. The form’s target=”IFrameName” is all that really matters.

As far as the IFrame. You can give it a width and height, and then any text that you generate in the php upload file will show there.

Really simple.

said on February 5th, 2008 at 16:45 · Permalink

Hi I have tried this in my app. In my app, I have a partial to display my form to upload files and in another partial i am listing the files uploaded. right now I have to redirect the page after uploading the files. But how display my file list partial initially and update it with adding the newly added file info every time i upload a new file…
any help appreciated

regards,
venkat

exip
said on November 21st, 2008 at 18:31 · Permalink

Есть плугин для jQuery, который использует похожую технику.

Comments are closed

Comments for this entry are closed for a while. If you have anything to say – use a contact form. Thank you for your patience.