multipart 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. data/multipart.rb +216 -0
  2. metadata +53 -0
@@ -0,0 +1,216 @@
1
+ # Multipart POST file upload for Net::HTTP::Post.
2
+ #
3
+ # By Leonardo Boiko <leoboiko@gmail.com>, public domain.
4
+ #
5
+ # Usage: see documentation for Net::HTTP::FileForPost and
6
+ # Net::HTTP::Post#set_multipart_data.
7
+
8
+ require 'net/http'
9
+ require 'stringio'
10
+
11
+ module Net
12
+ class HTTP
13
+
14
+ # When sending files via POST (rfc1867), the HTTP message has a
15
+ # special content-type: multipart/form-data. Its body includes
16
+ # zero or more key/value parameters like in other POST messages,
17
+ # plus one or more file upload parameters. File upload parameters
18
+ # are special:
19
+ #
20
+ # - A single parameter may carry more than one file. This
21
+ # technique is called multipart/mixed.
22
+ #
23
+ # - Each _file_ (not parameter!) can include extra metadata: its
24
+ # filename and mimetype.
25
+ #
26
+ # This class models the file arguments used in
27
+ # multipart/form-data. See Net::HTTP::Post#set_multipart_data for
28
+ # examples.
29
+ #
30
+ class FileForPost
31
+ attr_accessor :filename, :mimetype, :shouldclose
32
+ attr_reader :input
33
+
34
+ # Arguments:
35
+ #
36
+ # filepath_or_io:: The file to upload; either the pathname to
37
+ # open, or an IO object. If it's a pathname,
38
+ # it will be opened now and closed
39
+ # automatically later.
40
+ #
41
+ # mimetype:: The content-type. Per RFC defaults to
42
+ # 'application/octet-stream', but I recommend setting
43
+ # it explicitly.
44
+ #
45
+ # filename:: The file name to send to the remote server. If not
46
+ # supplied, will guess one based on the file's path.
47
+ # You can set it later with filename=.
48
+ #
49
+ def initialize(filepath_or_io,
50
+ mimetype='application/octet-stream',
51
+ filename=nil)
52
+ @mimetype = mimetype
53
+ @filename = nil
54
+
55
+ if filepath_or_io.respond_to? :read
56
+ @input = filepath_or_io
57
+ @shouldclose = false # came opened
58
+
59
+ if filepath_or_io.respond_to? :path
60
+ @filename = File.basename(filepath_or_io.path)
61
+ end
62
+
63
+ else
64
+ @input = File.open(filepath_or_io, 'rb')
65
+ @shouldclose = true # I opened it
66
+ @filename = File.basename(filepath_or_io)
67
+ end
68
+
69
+ end
70
+
71
+ def read(*args)
72
+ @input.read(*args)
73
+ end
74
+
75
+ def maybeclose
76
+ @input.close if @shouldclose
77
+ end
78
+ end
79
+
80
+ class Post
81
+
82
+ # Similar to Net::HTTP::Post#set_form_data (in Ruby's stardard
83
+ # library), but set up file upload parameters using the
84
+ # appropriate HTTP/HTML Forms multipart format.
85
+ #
86
+ # *Arguments*
87
+ #
88
+ # files_params:: A hash of file upload parameters. The keys are
89
+ # parameter names, and the values are
90
+ # Net::HTTP::FileForPost instances. See that
91
+ # class documentation for more info about how
92
+ # POST file upload works.
93
+ #
94
+ # other_params:: A hash of {key => value} pairs for the regular
95
+ # POST parameters, just like in set_form_data.
96
+ # Don't mix set_form_data and set_multipart_data;
97
+ # they'll overwrite each other's work.
98
+ #
99
+ # boundary1, boundary2:: A couple of strings which doesn't occur
100
+ # in your files. Boundary2 is only
101
+ # needed if you're using the
102
+ # multipart/mixed technique. The
103
+ # defaults should be OK for most cases.
104
+ #
105
+ # *Examples*
106
+ #
107
+ # Simplest case (single-parameter single-file), complete:
108
+ #
109
+ # require 'net/http'
110
+ # require 'rubygems'
111
+ # require 'multipart'
112
+ #
113
+ # req = Net::HTTP::Post.new('/scripts/upload.rb')
114
+ # req.basic_auth('jack', 'inflamed sense of rejection')
115
+ #
116
+ # file = Net::HTTP::FileForPost.new('/tmp/yourlife.txt', 'text/plain')
117
+ # req.set_multipart_data({:poem => file},
118
+ #
119
+ # {:author => 'jack',
120
+ # :user_agent => 'soapfactory'})
121
+ #
122
+ # res = Net::HTTP.new(url.host, url.port).start do |http|
123
+ # http.request(req)
124
+ # end
125
+ #
126
+ # Convoluted example:
127
+ #
128
+ # pic1 = Net::HTTP::FileForPost.new('pic1.jpeg', 'image/jpeg')
129
+ # pic2 = Net::HTTP::FileForPost.new(pic2_io, 'image/jpeg')
130
+ # pic3 = Net::HTTP::FileForPost.new('pic3.png', 'image/png')
131
+ # pic1_t = Net::HTTP::FileForPost.new('pic1_thumb.jpeg', 'image/jpeg')
132
+ # pic2_t = Net::HTTP::FileForPost.new(pic2_t_io, 'image/jpeg')
133
+ # desc = Net::HTTP::FileForPost.new('desc.html', 'text/html',
134
+ # 'index.html') # remote fname
135
+ #
136
+ # req.set_multipart_data({:gallery_description => des,
137
+ # :pictures => [pic1, pic2, pic3],
138
+ # :thumbnails => [pic1_t, pic2_t]},
139
+ #
140
+ # {:gallery_name => 'mygallery',
141
+ # :encoding => 'utf-8'})
142
+ #
143
+ def set_multipart_data(files_params,
144
+ other_params={},
145
+ boundary1="paranguaricutirimirruaru0xdeadbeef",
146
+ boundary2="paranguaricutirimirruaru0x20132")
147
+
148
+ self.content_type = "multipart/form-data, boundary=\"#{boundary1}\""
149
+
150
+ tmp = StringIO.new('r+b')
151
+
152
+ # let's do the easy ones first
153
+ other_params.each do |key,val|
154
+ tmp.write "\n--#{boundary1}\n"
155
+ tmp.write "content-disposition: form-data; name=\"#{key}\"\n"
156
+ tmp.write "\n"
157
+ tmp.write "#{val}"
158
+ end
159
+
160
+ # now handle the files...
161
+ files_params.each do |name, file|
162
+ tmp.write "\n--#{boundary1}\n"
163
+
164
+ # no \n
165
+ tmp.write "content-disposition: form-data; name=\"#{name}\""
166
+
167
+ if not file.is_a? Enumerable
168
+ # single-file multipart is different
169
+
170
+ if file.filename
171
+ # right in content-dispo line
172
+ tmp.write "; filename=\"#{file.filename}\"\n"
173
+ else
174
+ tmp.write "\n"
175
+ end
176
+
177
+ tmp.write "Content-Type: #{file.mimetype}\n"
178
+ tmp.write "Content-Transfer-Encoding: binary\n"
179
+ tmp.write "\n"
180
+ tmp.write(file.read())
181
+ file.maybeclose
182
+ else
183
+ # multiple-file parameter (multipart/mixed)
184
+ tmp.write "\n"
185
+ tmp.write "Content-Type: multipart/mixed,"
186
+ tmp.write " boundary=\"#{boundary2}\"\n"
187
+
188
+ file.each do |f|
189
+ tmp.write "\n--#{boundary2}\n"
190
+
191
+ tmp.write "Content-disposition: attachment"
192
+ if f.filename
193
+ tmp.write "; filename=\"#{f.filename}\"\n"
194
+ else
195
+ tmp.write "\n"
196
+ end
197
+
198
+ tmp.write "Content-Type: #{f.mimetype}\n"
199
+ tmp.write "Content-Transfer-Encoding: binary\n"
200
+ tmp.write "\n"
201
+ tmp.write(f.read())
202
+ f.maybeclose
203
+ end
204
+ tmp.write "\n--#{boundary2}\n"
205
+ end
206
+ end
207
+ tmp.write "\n--#{boundary1}\n"
208
+
209
+ tmp.flush.seek(0)
210
+
211
+ self.body_stream = tmp
212
+ nil
213
+ end
214
+ end
215
+ end
216
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multipart
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.2"
5
+ platform: ruby
6
+ authors:
7
+ - Leonardo Boiko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-08-05 00:00:00 -03:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Multipart is a gem that adds support to multipart/form-encoded and multipart/mixed (file upload) to Net::HTTP::Post. Nothing more, nothing less. Currently it supports a file param with multiple files, but not multiple file params.
17
+ email: leoboiko@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - multipart.rb
26
+ has_rdoc: true
27
+ homepage: http://rubyforge.org/projects/multipart
28
+ post_install_message:
29
+ rdoc_options: []
30
+
31
+ require_paths:
32
+ - .
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 1.8.3
38
+ version:
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ requirements: []
46
+
47
+ rubyforge_project: multipart
48
+ rubygems_version: 1.1.1
49
+ signing_key:
50
+ specification_version: 2
51
+ summary: add multipart (file upload) support to Net::HTTP::Post
52
+ test_files: []
53
+