Problems mixing regular form fields with file uploads

  • Thread starter David Heinemeier Hansson
  • Start date
D

David Heinemeier Hansson

I'm trying to build a form that includes both regular form fields, such
as text inputs and checkboxes, along with file uploading. It seems,
though, that all the fields are treated like a File when a file input
is used. I've tried to narrow it down with this script:

require 'CGI'
cgi = CGI.new

if cgi.params.length == 0
print "<html><body><form method='post'
enctype='multipart/form-data'>"
print "<input type='text' name='title'><input type='file'
name='logo'><input type='submit'>"
print "</form></body></html>"
else
$stderr << cgi.params['title'].first
$stderr << cgi.params['logo'].first.original_filename
end

Entering "A picture" in the title input and choosing a file called
"123.jpg" would add the following to the log: "A picture123.jpg".
Instead I get "#<File:0x40b7e8>123.jpg".

Entering "A picture" in the title input and not choosing a file
produces:
#<StringIO:0x209a52c>#<StringIO:0x20b3f04>

I'm baffled. Why is the title input treated first as a file and since
as a StringIO? I reckon it has something to do with the enctype, but I
can't seem to come up with a solution.

How would one go about extracting regular form data alongside file
handling?

Note, I'm using mod_ruby 1.1.1, Apache 1.3.x, and Ruby 1.8.0 on OS X
10.3.

// David
 
R

Robert Klemme

David Heinemeier Hansson said:
I'm trying to build a form that includes both regular form fields, such
as text inputs and checkboxes, along with file uploading. It seems,
though, that all the fields are treated like a File when a file input
is used. I've tried to narrow it down with this script:

require 'CGI'
cgi = CGI.new

if cgi.params.length == 0
print "<html><body><form method='post'
enctype='multipart/form-data'>"
print "<input type='text' name='title'><input type='file'
name='logo'><input type='submit'>"
print "</form></body></html>"
else
$stderr << cgi.params['title'].first
$stderr << cgi.params['logo'].first.original_filename
end

Entering "A picture" in the title input and choosing a file called
"123.jpg" would add the following to the log: "A picture123.jpg".
Instead I get "#<File:0x40b7e8>123.jpg".

Entering "A picture" in the title input and not choosing a file
produces:
#<StringIO:0x209a52c>#<StringIO:0x20b3f04>

I'm baffled. Why is the title input treated first as a file and since
as a StringIO? I reckon it has something to do with the enctype, but I
can't seem to come up with a solution.

How would one go about extracting regular form data alongside file
handling?

Note, I'm using mod_ruby 1.1.1, Apache 1.3.x, and Ruby 1.8.0 on OS X
10.3.

If you are using 'multipart/form-data' - which you have to if you want to
upload files via HTTP - all your data (files and input field content) is
sent in a single stream that contains all the data - file content mixed
with field content. I guess the CGI lib just decodes and copies all
content into a series of tmp files if it encounters 'multipart/form-data'
with the optimization that StringIO instances are used if the amount
doesn't get too big (or nor files are used).

In short, you'll have to read all content (file as well as field) as if
reading from an IO if you use 'multipart/form-data'. That's the simples
solution IMHO.

Regards

robert
 
D

David Heinemeier Hansson

I've narrowed the problem down even further. It appears to be a problem
with the total length of the request. If I upload a 10K file and a
"hehe" in the text, both are presented as StringIO objects that can be
read.

If I upload a 15K file and a "hehe" the file is presented as a
StringIO, but the "hehe" as a File object.

If I upload a 20K file and a "hehe" both are presented as File objects.

Additionally, as soon as either of the fields switch to the file object
representation they're representing empty files. So doing a .size
returns 0, whereas the correct size is returned on the StringIO
objects.

I've tried this on two different setups now. Ruby 1.8.0/mod_ruby
1.0.7/OS X 10.3 and on a Ruby 1.8.0/mod_ruby 1.1.1/FreeBSD 4.8. Both
exhibit the exact same symptoms.

So even though I'm somewhat wiser of the problem cause, I'm even more
baffled about what to do.

Unlike PHP, it doesn't seem like mod_ruby exposes any
upload_max_filesize or post_max_size variables. And in any case, why
would they be set so low (around 16K) on default? And what's the point
in switching from a StringIO to a File object representation?

So many questions... (hopefully at least some answers, this time ;))

I'm trying to build a form that includes both regular form fields,
such as text inputs and checkboxes, along with file uploading. It
seems, though, that all the fields are treated like a File when a file
input is used. I've tried to narrow it down with this script:

require 'CGI'
cgi = CGI.new

if cgi.params.length == 0
print "<html><body><form method='post'
enctype='multipart/form-data'>"
print "<input type='text' name='title'><input type='file'
name='logo'><input type='submit'>"
print "</form></body></html>"
else
$stderr << cgi.params['title'].first
$stderr << cgi.params['logo'].first.original_filename
end

Entering "A picture" in the title input and choosing a file called
"123.jpg" would add the following to the log: "A picture123.jpg".
Instead I get "#<File:0x40b7e8>123.jpg".

Entering "A picture" in the title input and not choosing a file
produces:
#<StringIO:0x209a52c>#<StringIO:0x20b3f04>

I'm baffled. Why is the title input treated first as a file and since
as a StringIO? I reckon it has something to do with the enctype, but I
can't seem to come up with a solution.

How would one go about extracting regular form data alongside file
handling?

Note, I'm using mod_ruby 1.1.1, Apache 1.3.x, and Ruby 1.8.0 on OS X
10.3.

// David



--
David Heinemeier Hansson.
http://www.loudthinking.com/ -- Broadcasting Brain
 
R

Robert Klemme

David Heinemeier Hansson said:
I've narrowed the problem down even further. It appears to be a problem
with the total length of the request. If I upload a 10K file and a
"hehe" in the text, both are presented as StringIO objects that can be
read.

If I upload a 15K file and a "hehe" the file is presented as a
StringIO, but the "hehe" as a File object.

If I upload a 20K file and a "hehe" both are presented as File objects.

Additionally, as soon as either of the fields switch to the file object
representation they're representing empty files. So doing a .size
returns 0, whereas the correct size is returned on the StringIO
objects.

I've tried this on two different setups now. Ruby 1.8.0/mod_ruby
1.0.7/OS X 10.3 and on a Ruby 1.8.0/mod_ruby 1.1.1/FreeBSD 4.8. Both
exhibit the exact same symptoms.

So even though I'm somewhat wiser of the problem cause, I'm even more
baffled about what to do.

Simply assume you are reading from an IO if it's multipart/form. And if
you need the complete content just use read (see below).
Unlike PHP, it doesn't seem like mod_ruby exposes any
upload_max_filesize or post_max_size variables. And in any case, why
would they be set so low (around 16K) on default? And what's the point
in switching from a StringIO to a File object representation?

Both support the same interface. So client code can read the whole
contents without any change, regardless whether the content sits in mem or
in a file. If you look at the doc of class CGI, you'll find this:

"When dealing with a multipart form, the array returned by CGI#[] is
composed of objects of class Tempfile, with the following dynamically
added methods:

read Body
local_path Path to local file containing the content
original_filename Original filename of the content
content_type Content type"


http://www.rubycentral.com/book/lib_network.html

Thus, as soon as the encoding is multipart, you should read the contents
from an IO, regardless whether it's a simple parameter or a file. If the
implementation chooses to use a StringIO in 1.8.x (previous versions don't
have it), this is simply an optimization. Dunno whether you can set the
max size somewhere...

IMHO this is pretty much straightforward. Don't let types confuse you
(see http://c2.com/cgi/wiki?DuckTyping )

Regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top