module HTTP::FormData
Overview
Contains utilities for parsing multipart/form-data
messages, which are
commonly used for encoding HTML form data.
Examples
Commonly, you'll want to parse a from response from a HTTP request, and process it. An example server which performs this task is shown below.
require "http"
require "tempfile"
server = HTTP::Server.new do |context|
name = nil
file = nil
HTTP::FormData.parse(context.request) do |part|
case part.name
when "name"
name = part.body.gets_to_end
when "file"
file = Tempfile.open("upload") do |file|
IO.copy(part.body, file)
end
end
end
unless name && file
context.response.status_code = 400
next
end
context.response << file.path
end
server.bind_tcp 8085
server.listen
To test the server, use the curl command below.
$ curl http://localhost:8085/ -F name=foo -F file=@/path/to/test.file
/tmp/upload.Yxn7cc
Another common case is sending formdata to a server using HTTP::Client. Here is an example showing how to upload a file to the server above in crystal.
require "http"
IO.pipe do |reader, writer|
channel = Channel(String).new(1)
spawn do
HTTP::FormData.build(writer) do |formdata|
channel.send(formdata.content_type)
formdata.field("name", "foo")
File.open("foo.png") do |file|
metadata = HTTP::FormData::FileMetadata.new(filename: "foo.png")
headers = HTTP::Headers{"Content-Type" => "image/png"}
formdata.file("file", file, metadata, headers)
end
end
writer.close
end
headers = HTTP::Headers{"Content-Type" => channel.receive}
response = HTTP::Client.post("http://localhost:8085/", body: reader, headers: headers)
puts "Response code #{response.status_code}"
puts "File path: #{response.body}"
end
Defined in:
http/formdata/builder.crhttp/formdata/parser.cr
http/formdata/part.cr
http/formdata.cr
Class Method Summary
-
.build(response : HTTP::Server::Response, boundary = Multipart.generate_boundary, &block)
Builds a multipart/form-data message, yielding a
FormData::Builder
object to the block which writes to response using *boundary. -
.build(io, boundary = Multipart.generate_boundary, &block)
Builds a multipart/form-data message, yielding a
FormData::Builder
object to the block which writes to io using boundary. -
.parse(io, boundary, &block)
Parses a multipart/form-data message, yielding a
FormData::Parser
. -
.parse(request : HTTP::Request, &block)
Parses a multipart/form-data message, yielding a
FormData::Parser
. -
.parse_content_disposition(content_disposition) : Tuple(String, FileMetadata)
Parses a
Content-Disposition
header string into a field name andFileMetadata
.
Class Method Detail
Builds a multipart/form-data message, yielding a FormData::Builder
object to the block which writes to response using *boundary.
Content-Type is set on response and Builder#finish
is called on the
builder when the block returns.
io = IO::Memory.new
response = HTTP::Server::Response.new io
HTTP::FormData.build(response, "boundary") do |builder|
builder.field("foo", "bar")
end
response.close
response.headers["Content-Type"] # => "multipart/form-data; boundary=\"boundary\""
io.to_s # => "HTTP/1.1 200 OK\r\nContent-Type: multipart/form-data; boundary=\"boundary\"\r\nContent-Length: 75\r\n\r\n--boundary\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n--boundary--"
See: FormData::Builder
Builds a multipart/form-data message, yielding a FormData::Builder
object to the block which writes to io using boundary.
Builder#finish
is called on the builder when the block returns.
io = IO::Memory.new
HTTP::FormData.build(io, "boundary") do |builder|
builder.field("foo", "bar")
end
io.to_s # => "--boundary\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n--boundary--"
See: FormData::Builder
Parses a multipart/form-data message, yielding a FormData::Parser
.
form_data = "--aA40\r\nContent-Disposition: form-data; name=\"field1\"\r\n\r\nfield data\r\n--aA40--"
HTTP::FormData.parse(IO::Memory.new(form_data), "aA40") do |part|
part.name # => "field1"
part.body.gets_to_end # => "field data"
end
See: FormData::Parser
Parses a multipart/form-data message, yielding a FormData::Parser
.
headers = HTTP::Headers{"Content-Type" => "multipart/form-data; boundary=aA40"}
body = "--aA40\r\nContent-Disposition: form-data; name=\"field1\"\r\n\r\nfield data\r\n--aA40--"
request = HTTP::Request.new("POST", "/", headers, body)
HTTP::FormData.parse(request) do |part|
part.name # => "field1"
part.body.gets_to_end # => "field data"
end
See: FormData::Parser
Parses a Content-Disposition
header string into a field name and
FileMetadata
. Please note that the Content-Disposition
header for
multipart/form-data
is not compatible with the original definition in
RFC 2183, but are instead specified
in RFC 2388.