On Fri, 10 Feb 2006, Michael Schilling wrote:

> Hello again,
>
> as I couldn't reproduce the described multipart_form error (see
> description below) directly on the command-line I looked into the cgi.rb
> source and found the chunk of code that raises the exception:

the problem is in your source i'm afraid.  you can call CGI.new more than once
in a script : each call attempts to parse ENV and STDIN according to the cgi
spec.  check out the docs for details.  the other issue is that if, and only
if, you create a form using multipar the cgi params are either StringIO or
Tempfile objects.  this is a good thing.  consider a 10GB file upload.  you
need a way to determine this.  following is one way:


   harp:~> cat a.rb

   #! /usr/local/bin/ruby

   require "cgi"

   class Upload
     attr_accessor 'cgi'

     def initialize
       self.cgi = CGI::new 'html4'
       file = get 'file'
       file ? answer : make_form
     end

     def make_form
      cgi.out {
        cgi.html {
          cgi.body { "\n" +
            cgi.multipart_form{ "\n" +
            cgi.file_field("file",40,500000) + "\n" +
            cgi.br +
            cgi.submit("test it") + "\n"
            }
          }
        }
      }
     end

     def answer
      cgi.out {
        cgi.html {
          cgi.body{ "\n" +
          cgi.p{ "This should be the filename : " + param('file').original_filename + "\n" } +
          cgi.br
          }
        }
      }
     end

     def get key
       value = param key
       value = value.read.strip if value.respond_to?('read')
       value
     end

     def param key
       cgi.params[key].first
     end
   end

   Upload::new if $0 == __FILE__


this should run with ruby 1.8 up.

inlined below is a totally simple upload script i sometimes use for personal
stuff.  it allows upload/download and some other stuff.

regards.

-a

-- 
happiness is not something ready-made.  it comes from your own actions.
- h.h. the 14th dali lama



#! /usr/local/bin/ruby

require 'cgi'
require 'tempfile'
require 'ftools'
require 'uri'

cgi     = CGI::new
user    = ENV['REMOTE_USER'] || 'anonymous'
headers = {}
content = ''

begin
   mode = cgi.params['m'].first || 'index'
   mode = mode.read.strip if mode.respond_to? 'read'

   case mode
     when /index|^$/

       files = Dir['**/*'].select{|f| f !~ %r/html|cgi/}
       dirname = File::dirname cgi.script_name

       files.sort!{|a,b| File::stat(b).mtime <=> File::stat(a).mtime}

       table_rows =
         files.collect do |f|
           stat = File::stat f
           uri = 'http://' << cgi.host << File::join(dirname, f)
           # <a href=#{ URI::escape f } title="right click -> save as">download</a>
           <<-html
             <tr valign=top aligh=left>
               <td>
                 #{ CGI::escapeHTML f }
               </td>
               <td>
                 #{ CGI::escapeHTML stat.mtime.to_s }
               </td>
               <td>
                 #{ CGI::escapeHTML stat.size.to_s }
               </td>
               <td>
                 <a href=#{ uri } title="right click -> save (page) as">#{ uri }</a>
               </td>
             </tr>
           html
         end

       table =
         <<-html
           <table width=100%>
             <tr valign=top aligh=left>
               <td> <b><u> file </b></u> </td>
               <td> <b><u> modification time </b></u> </td>
               <td> <b><u> size </b></u> </td>
               <td> <b><u> download </b></u> </td>
             </tr>
             #{ table_rows }
           </table>
         html

       content = <<-html
         <html>
           <head>
             <title>#{ user }::files</title>
           </head>
           <body>
             <h3>#{ user }::files</h3>

             <table cellpadding=1 align=right width=100%>
               <tr align=right>
                 <td>
                   <i>to download - right click -> save as</i>
                 </td>
               </tr>
             </table>

             <hr>

             <hr>
             </table>
             <hr>
               #{ table }
             <hr>
             <h3><a href=#{ cgi.script_name }?m=upload>upload files</a></h3>
           </body>
         </html>
       html

     when /upload/
       buttons = []
       8.times do
         buttons << <<-html
           <input size=100 name=file type=file>
           <hr width=30% align=left>
         html
       end
       content = <<-html
         <html>
           <head>
             <title>#{ user }::upload</title>
           </head>
           <body>
             <h3>#{ user }::upload</h3>
             <hr>
             <form action=#{ cgi.script_name } method=POST enctype=multipart/form-data>
               <input size=100 name=file type=file>
               <hr width=30% align=left>
               #{ buttons }

               <input type=submit value=upload>
               <hr width=30% align=left>

               <input type=reset value=clear>
               <hr width=30% align=left>

               <input type=hidden name=m value=put>
             <form>
             <hr>
             <h3><a href=#{ cgi.script_name }?m=index>index</a></h3>
           </body>
         </html>
       html

     when /put/
       uploaded = []
       tempfiles = cgi.params['file']
       tempfiles.each do |tempfile|
         org_path = tempfile.original_filename

         local = "#{ org_path }"
         local.gsub! %r|[/\\]|,'/'
         local.gsub! %r|\s+|,'_'
         local.downcase!

         next if local =~ /^\s*$/

         begin
           File::mkpath(File::dirname(local)) rescue nil
           if tempfile.path
             File::copy tempfile.path, local
           else
             open(local,'w'){|f| f.write tempfile.read}
           end
           File::chmod 0777, local
           uploaded << [org_path, true]
         rescue
           raise
           raise tempfile.methods.inspect
           uploaded << [org_path, false]
         end
       end

       table_rows =
         uploaded.collect do |u|
           f, status = u
           s = (status ? 'success' : 'failed')
           <<-html
             <tr valign=top aligh=left>
               <td>
                 #{ CGI::escapeHTML f.inspect }
               </td>
               <td>
                 #{ s }
               </td>
             </tr>
           html
         end

       table =
         <<-html
           <table width=100%>
             <tr valign=top aligh=left>
               <td> <b><u> file </b></u> </td>
               <td> <b><u> status </b></u> </td>
             </tr>
             #{ table_rows }
           </table>
         html

       content = <<-html
         <html>
           <head>
             <title>#{ user }::uploaded</title>
           </head>
           <body>
             <h3>#{ user }::uploaded</h3>
             </table>
             <hr>
               #{ table }
             <hr>
             <h3><a href=#{ cgi.script_name }?m=index>index</a></h3>
           </body>
         </html>
       html


     else
       content = "#{ cgi.inspect }"
       content << '<hr>'
       content << cgi.params.inspect
       content << '<hr>'
       content << cgi.params.map{|key,value| [key.inspect, value.map{|v| [v.inspect, v.class]}.inspect]}.inspect
   end

rescue Exception => e
   content = "<b>#{ e.message } - (#{ e.class })</b><hr>#{ e.backtrace.join '<br>' }"
ensure
   cgi.out{ content.to_s  }
end