Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 7759

Show
Ignore:
Timestamp:
10/06/07 11:40:13 (2 years ago)
Author:
bitsweat
Message:

Use StringIO and Tempfile subclasses instead of defining singleton methods on each multipart field.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/lib/action_controller/request.rb

    r7758 r7759  
    460460              value.map { |v| get_typed_value(v) } 
    461461            else 
    462               # This is an uploaded file. 
    463               if value.respond_to?(:original_filename) && !value.original_filename.blank? 
    464                 unless value.respond_to?(:full_original_filename) 
    465                   class << value 
    466                     alias_method :full_original_filename, :original_filename 
    467  
    468                     # Take the basename of the upload's original filename. 
    469                     # This handles the full Windows paths given by Internet Explorer 
    470                     # (and perhaps other broken user agents) without affecting 
    471                     # those which give the lone filename. 
    472                     # The Windows regexp is adapted from Perl's File::Basename. 
    473                     def original_filename 
    474                       if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename) 
    475                         md.captures.first 
    476                       else 
    477                         File.basename full_original_filename 
    478                       end 
    479                     end 
    480                   end 
     462              if value.is_a?(UploadedFile) 
     463                # Uploaded file 
     464                if value.original_filename 
     465                  value 
     466                # Multipart param 
     467                else 
     468                  result = value.read 
     469                  value.rewind 
     470                  result 
    481471                end 
    482  
    483                 # Return the same value after overriding original_filename. 
    484                 value 
    485  
    486               # Multipart values may have content type, but no filename. 
    487               elsif value.respond_to?(:read) 
    488                 result = value.read 
    489                 value.rewind 
    490                 result 
    491  
    492472              # Unknown value, neither string nor multipart. 
    493473              else 
     
    525505            content = 
    526506              if 10240 < content_length 
    527                 Tempfile.new("CGI") 
     507                UploadedTempfile.new("CGI") 
    528508              else 
    529                 StringIO.new 
     509                UploadedStringIO.new 
    530510              end 
    531511            content.binmode if defined? content.binmode 
     
    569549            content.rewind 
    570550 
    571             /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni.match(head) 
    572             filename = ($1 or $2 or "") 
    573             if /Mac/ni.match(env['HTTP_USER_AGENT']) and 
    574                 /Mozilla/ni.match(env['HTTP_USER_AGENT']) and 
    575                 (not /MSIE/ni.match(env['HTTP_USER_AGENT'])) 
    576               filename = CGI.unescape(filename) 
     551            head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni 
     552            if filename = $1 || $2 
     553              if /Mac/ni.match(env['HTTP_USER_AGENT']) and 
     554                  /Mozilla/ni.match(env['HTTP_USER_AGENT']) and 
     555                  (not /MSIE/ni.match(env['HTTP_USER_AGENT'])) 
     556                filename = CGI.unescape(filename) 
     557              end 
     558              content.original_path = filename.dup 
    577559            end 
    578560 
    579             /Content-Type: ([^\r]*)/ni.match(head) 
    580             content_type = ($1 or "") 
    581  
    582             (class << content; self; end).class_eval do 
    583               alias local_path path 
    584               define_method(:original_filename) {filename.dup.taint} 
    585               define_method(:content_type) {content_type.dup.taint} 
    586             end 
    587  
    588             /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head) 
    589             name = $1.dup 
     561            head =~ /Content-Type: ([^\r]*)/ni 
     562            content.content_type = $1.dup if $1 
     563 
     564            head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni 
     565            name = $1.dup if $1 
    590566 
    591567            if params.has_key?(name) 
     
    696672      end 
    697673  end 
     674 
     675  module UploadedFile 
     676    def self.included(base) 
     677      base.class_eval do 
     678        attr_accessor :original_path, :content_type 
     679        alias_method :local_path, :path 
     680      end 
     681    end 
     682 
     683    # Take the basename of the upload's original filename. 
     684    # This handles the full Windows paths given by Internet Explorer 
     685    # (and perhaps other broken user agents) without affecting 
     686    # those which give the lone filename. 
     687    # The Windows regexp is adapted from Perl's File::Basename. 
     688    def original_filename 
     689      unless defined? @original_filename 
     690        @original_filename = 
     691          unless original_path.blank? 
     692            if original_path =~ /^(?:.*[:\\\/])?(.*)/m 
     693              $1 
     694            else 
     695              File.basename original_path 
     696            end 
     697          end 
     698      end 
     699      @original_filename 
     700    end 
     701  end 
     702 
     703  class UploadedStringIO < StringIO 
     704    include UploadedFile 
     705  end 
     706 
     707  class UploadedTempfile < Tempfile 
     708    include UploadedFile 
     709  end 
    698710end 
  • trunk/actionpack/test/controller/request_test.rb

    r7758 r7759  
    528528  end 
    529529 
     530  UploadedStringIO = ActionController::UploadedStringIO 
     531  class MockUpload < UploadedStringIO 
     532    def initialize(content_type, original_path, *args) 
     533      self.content_type = content_type 
     534      self.original_path = original_path 
     535      super *args 
     536    end 
     537  end 
     538 
    530539  def test_parse_params_from_multipart_upload 
    531     mockup = Struct.new(:content_type, :original_filename, :read, :rewind) 
    532     file = mockup.new('img/jpeg', 'foo.jpg') 
    533     ie_file = mockup.new('img/jpeg', 'c:\\Documents and Settings\\foo\\Desktop\\bar.jpg') 
    534     non_file_text_part = mockup.new('text/plain', '', 'abc') 
     540    file = MockUpload.new('img/jpeg', 'foo.jpg') 
     541    ie_file = MockUpload.new('img/jpeg', 'c:\\Documents and Settings\\foo\\Desktop\\bar.jpg') 
     542    non_file_text_part = MockUpload.new('text/plain', '', 'abc') 
    535543 
    536544    input = { 
    537       "something" => [ StringIO.new("") ], 
    538       "array_of_stringios" => [[ StringIO.new("One"), StringIO.new("Two") ]], 
    539       "mixed_types_array" => [[ StringIO.new("Three"), "NotStringIO" ]], 
    540       "mixed_types_as_checkboxes[strings][nested]" => [[ file, "String", StringIO.new("StringIO")]], 
    541       "ie_mixed_types_as_checkboxes[strings][nested]" => [[ ie_file, "String", StringIO.new("StringIO")]], 
    542       "products[string]" => [ StringIO.new("Apple Computer") ], 
     545      "something" => [ UploadedStringIO.new("") ], 
     546      "array_of_stringios" => [[ UploadedStringIO.new("One"), UploadedStringIO.new("Two") ]], 
     547      "mixed_types_array" => [[ UploadedStringIO.new("Three"), "NotStringIO" ]], 
     548      "mixed_types_as_checkboxes[strings][nested]" => [[ file, "String", UploadedStringIO.new("StringIO")]], 
     549      "ie_mixed_types_as_checkboxes[strings][nested]" => [[ ie_file, "String", UploadedStringIO.new("StringIO")]], 
     550      "products[string]" => [ UploadedStringIO.new("Apple Computer") ], 
    543551      "products[file]" => [ file ], 
    544       "ie_products[string]" => [ StringIO.new("Microsoft") ], 
     552      "ie_products[string]" => [ UploadedStringIO.new("Microsoft") ], 
    545553      "ie_products[file]" => [ ie_file ], 
    546554      "text_part" => [non_file_text_part] 
     
    696704    assert_kind_of StringIO, file 
    697705    assert_equal 'file.csv', file.original_filename 
    698     assert_equal '', file.content_type 
     706    assert_nil file.content_type 
    699707    assert_equal 'contents', file.read 
    700708