Search code examples
ruby-on-railsrubyformsinputfile-upload

Rails Form with File Input Field - File just Name as String


I want to embed a file input field in my form. But on submit, the param value is just the file name, no StringIO or anything else, just a String with Filename.

Form:

    <%= form_tag(controller: 'search', action: 'confirm_new_search', method: 'post', multipart: true) do %>
        [....]
        <div class="custom-file field" id="inc_form">
          <%= file_field :post, :file_inc_sellerslist, class: "custom-file-input" %>
          <label class="custom-file-label" for="file_inc_sellerslist">
            <%= t('dashboard.new_search.extra_search_options_entries.choose_file') %>
          </label>
        </div>
        [....]

What is wrong there?


Solution

  • Your form_tag() syntax is incorrect. form_tag takes two hashes as arguments: one for the url, one for the html attributes.

    form_tag(url_for_options = {}, options = {}, &block)

    Starts a form tag that points the action to a url configured with url_for_options just like ActionController::Base#url_for. The method for the form defaults to POST.

    Options

    :multipart - If set to true, the enctype is set to “multipart/form-data”.

    :method - The method to use when submitting the form, usually either “get” or “post”. If “patch”, “put”, “delete”, or another verb is used, a hidden input with name _method is added to simulate the verb over post.

    :authenticity_token - Authenticity token to use in the form. Use only if you need to pass custom authenticity token string, or to not add authenticity_token field at all (by passing false). Remote forms may omit the embedded authenticity token by setting config.action_view.embed_authenticity_token_in_remote_forms = false. This is helpful when you're fragment-caching the form. Remote forms get the authenticity token from the meta tag, so embedding is unnecessary unless you support browsers without JavaScript.

    :remote - If set to true, will allow the Unobtrusive JavaScript drivers to control the submit behavior. By default this behavior is an ajax submit.

    :enforce_utf8 - If set to false, a hidden input with name utf8 is not output.

    Any other key creates standard HTML attributes for the tag.

    http://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag

    The following works for me:

    <%= form_tag({controller: 'search', action: 'confirm_new_search'},
      method: 'post', 
      multipart: true) do %>
    

    Here is an example of what happens with your form_tag() call:

    def go(x={}, y={})
      p x
      p y
    end
    
    go(a: 1, b:2, c:3)
    
    --output:
    {:a=>1, :b=>2, :c=>3}
    {}
    

    As a result, all the key/value pairs you specified for form_for() are gathered into a hash and assigned to the parameter variable url_for_options. The consequence of that is that your form_tag() isn't setting the html option multipart to true. If you look at the source for your html, you will see:

    <form action="/search/confirm_new_search?method=post&amp;multipart=true" 
          accept-charset="UTF-8" 
          method="post">
    

    If you compare that html to your form_tag() call:

    form_tag(controller: 'search', 
             action: 'confirm_new_search', 
             method: 'post', 
             multipart: true)
    

    it's apparent that the last two key/value pairs get added to the query string in the url. That behavior doesn't seem to be documented anywhere, but the rule seems to be: for any key that url_for() doesn't recognize, the key and the corresponding value get added to the query string. And, key/value pairs in the query string have nothing to do with html attributes for the <form> tag.