I think that using a `data-procedure/c` of a particular sort should allow you to implement this without needing access to the struct internals or needing to read everything into memory at once.
(Though, it would be a bit nicer if the write proc allowed you to specify an offset and length into the string/byte string.)
#lang racket/base
(require net/http-client
         file/sha1
         racket/random)
(define (http-conn-send/multipart! hc uri multipart-body
                                   #:version [version #"1.1"]
                                   #:method [method #"POST"]
                                   #:close? [close? #f]
                                   #:headers [headers '()]
                                   #:content-decode [decodes '(gzip)]
                                   #:boundary [boundary (random-boundary)])
  (define content-type-header
    (string-append
     "Content-Type: multipart/form-data; boundary="
     boundary))
      
  (http-conn-send! hc uri
                   #:version version
                   #:method method
                   #:close? close?
                   #:headers (cons content-type-header headers)
                   #:content-decode decodes
                   #:data (multipart-body->data-proc boundary multipart-body)))
(define (mime-escape s)
  (regexp-replace* #rx"[\"\\]" s "\\\\\\0"))
(define (make-string-part field-name field-value)
  (λ (write-chunk boundary)
    (write-chunk
     (format
      (string-append "--~a\r\n"
                     "Content-Disposition: form-data; name=\"~a\"\r\n"
                      "Content-Type: text/plain; charset=utf-8\r\n"
                      "\r\n"
                      "~a\r\n")
      boundary
      (mime-escape field-name)
      field-value))))
(define (make-file-part field-name file-name content-type in)
  (λ (write-chunk boundary)
    (write-chunk
     (format
      (string-append "--~a\r\n"
                     "Content-Disposition: form-data; name=\"~a\"; filename=\"~a\"\r\n"
                      "Content-Type: ~a\r\n"
                      "\r\n")
      boundary
      (mime-escape field-name)
      (mime-escape file-name)
      content-type))
    (define buffer (make-bytes 4096))
    (let loop ([n (read-bytes-avail! buffer in)])
      (cond
        [(eof-object? n)
         n]
        [else
         (write-chunk (subbytes buffer 0 n))
         (loop (read-bytes-avail! buffer in))]))
    (write-chunk "\r\n")))
(define (multipart-body->data-proc boundary parts)
  (λ (write-chunk)
    (for ([part parts])
      (part write-chunk boundary))
    (write-chunk (format "--~a--\r\n" boundary))))
      
(define (random-boundary)
  (string-append
   "--------------------------"
   (bytes->hex-string
    (crypto-random-bytes 8))))