Search code examples
ruby-on-railsformsfile-iohttp-method

param identified as a string rather than a file


In my view file submit.html.erb, I decided to use the <form> tag straightforwardly, instead of the form helpers included with rails. Here is the view file:

<form action = "/winter" method="post">
<input type="file" name="doc">
<p> Upload your question. </p>
<input type = "submit">
 <input name="authenticity_token" type="hidden" 
      value="....">
</form>

Submitting this form invokes a post request on /winters with parameters

{"doc"=>"1.docx", "authenticity_token"=>"...."}

While I expected the doc parameter to be the file 1.docx I uploaded, the doc parameter continues to be the string original_filename of file 1.docx.

My routes.rb file has the code

post "/winter" => "paper#submit"

while inside the submit method of paper_controller is the code:

File.write("Papers/rain.docx", params[:doc].read)
redirect_to "/paper"

And correspondingly, when I submit the aforementioned form, I go the /winters url and get the error

undefined method 'read' for "1.docx":String

So why is the doc parameter set as the filename rather than the file itself? This is in contrary to the docs, I suppose: [http://guides.rubyonrails.org/form_helpers.html#what-gets-uploaded]

One more point, probably relevant here, is that, there in the doc's first paragraph it says:

Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file.

So, is it saying that sometimes the params[:doc] might be a file instance, other times it might be a string? How to deal with such random behavior?

And, one more thing, what if I tried get request instead of put request? get has to put the params right after the url, as a query string, right? So should then params[:doc] be always a string? I tried using get, and was sent to the url

http://localhost:3000/winter?doc=1.docx

(I wasn't using the authenticity token params' hidden input then). And of course, the same

undefined method 'read' for "1.docx":String

error occurred.


Solution

  • You have to use multipart/formdata encoding for file transfers to work. This is true for any web framework.

    I would suggest you just use the form helpers as it will add the correct authenticity token:

    <%= form_tag("/winter", multipart: true) do %>
      <%= file_field_tag 'doc' %>
      # ...
    <% end %>
    

    And, one more thing, what if I tried get request instead of put request? get has to put the params right after the url, as a query string, right? So should then params[:doc] be always a string? I tried using get, and was sent to the url

    You cannot upload files in a GET request. They must be included in the request body of a POST/PATCH/PUT request with multipart/formdata encoding.

    Files are binary - the query string which is used to pass data in GET requests is just a string containing formdata key/value pairs. Thus to pass a file in a GET request you would need to base64 encode it which inflates the file size by ~30%. Many browsers also limit the length of URIs as do some web servers to prevent DOS attacks which makes it a really bad idea.