Module: Scanii::Multipart
- Defined in:
- lib/scanii/multipart.rb
Overview
Hand-rolled multipart/form-data encoder (RFC 7578).
Ruby’s stdlib Net::HTTP does not bundle a multipart encoder; this is the smallest viable implementation that covers the Scanii POST /files payload.
Body is assembled as a single binary-encoded String – file contents are read into memory rather than streamed. This matches the PHP SDK’s approach; callers scanning very large files should be aware.
Class Method Summary collapse
-
.encode(fields, file_path, file_field: "file") ⇒ Array(String, String)
Encode a multipart body containing the bytes at file_path plus the given text fields.
-
.guess_content_type(path) ⇒ Object
Best-effort content-type lookup by extension.
-
.make_boundary ⇒ Object
Generate a unique multipart boundary.
-
.make_content_type(boundary) ⇒ Object
Build the Content-Type header value for a request using boundary.
Class Method Details
.encode(fields, file_path, file_field: "file") ⇒ Array(String, String)
Encode a multipart body containing the bytes at file_path plus the given text fields.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/scanii/multipart.rb', line 32 def encode(fields, file_path, file_field: "file") boundary = make_boundary filename = File.basename(file_path) content_type = guess_content_type(file_path) file_bytes = File.binread(file_path) body = String.new(encoding: Encoding::BINARY) fields.each do |name, value| write_text_part(body, boundary, name.to_s, value.to_s) end body << "--#{boundary}\r\n".b body << "Content-Disposition: form-data; name=\"#{file_field}\"; filename=\"#{filename}\"\r\n".b body << "Content-Type: #{content_type}\r\n\r\n".b body << file_bytes.b body << "\r\n".b body << "--#{boundary}--\r\n".b [body, make_content_type(boundary)] end |
.guess_content_type(path) ⇒ Object
Best-effort content-type lookup by extension. Falls back to application/octet-stream. The Scanii API does not require an accurate content-type on the multipart part – the server inspects the bytes – so a short table is sufficient.
59 60 61 62 |
# File 'lib/scanii/multipart.rb', line 59 def guess_content_type(path) ext = File.extname(path).delete_prefix(".").downcase MIME_TYPES.fetch(ext, "application/octet-stream") end |
.make_boundary ⇒ Object
Generate a unique multipart boundary.
16 17 18 |
# File 'lib/scanii/multipart.rb', line 16 def make_boundary "----scanii-ruby-boundary-#{SecureRandom.hex(16)}" end |
.make_content_type(boundary) ⇒ Object
Build the Content-Type header value for a request using boundary.
21 22 23 |
# File 'lib/scanii/multipart.rb', line 21 def make_content_type(boundary) "multipart/form-data; boundary=#{boundary}" end |