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.
- 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
|
+
|