multipart 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/multipart.rb +216 -0
- metadata +53 -0
data/multipart.rb
ADDED
@@ -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
|
+
|