Create Proper Multi-Part Form Data for HTTP Transfers

I’ve been working on transferring some image data in my iOS app using a Xojo.Net.HTTP Socket and a POST command. I had trouble and posted about it elsewhere. Thanks to @Andrew_Lambert, I found out that Xojo does not create multi-part form data for HTML when you set the content type. You have to do this yourself. Andrew had written a method but it was for the legacy HTTPSocket and didn’t work with newer sockets such as the URLConnection or Xojo.Net.HTTPSocket for iOS. I have modified Andrew’s code to make it compatible with the Xojo.Net.HTTPSocket and it should work fine for the URLConnection as well - just need to change the signature in the method.

Pass in your content sections as Dictionary pairs.

I hope this is of use to people.

Public Sub SetFormData(extends sock as Xojo.Net.HTTPSocket, contenttype as text, FormData as Xojo.Core.Dictionary, Boundary as Text = "")
  // Original code for legacy HTTPSocket written by Andrew Lambert
  // This code modified by Jon Ogden, Just Add Software, LLC - September 2020 for iOS and Xojo.Net.HTTPSocket
  // Could also be used on URLConnection - need to modify the signature and make sure you use Xojo.Core - Using statement included
  // Added in the ability to set the content type in the parameters to the method.  This was hard coded previously.
  // Added the ability to send and process data in a memory block - the original method handled text and folder items.
  // Code properly creates multi-part form data.
  Using Xojo.Core
  // if the user doesn't include a boundary string then create one.
  If Boundary.Trim = "" Then
    Dim m as MemoryBlock = TextEncoding.ASCII.ConvertTextToData(Microseconds.ToText)
    Dim m2 as MemoryBlock = MD5(m)
    Dim MD5Digest as Text = TextEncoding.ASCII.ConvertDataToText(MD5(m),True)
    Boundary = "--" + EncodeHex(MD5(m)).RIght(24)+ "-bOuNdArY"
  End If
  // create some of the local variables 
  Static CRLF As Text = &u0D+&u0A
  Dim data As New MemoryBlock(0)
  Dim out As New BinaryStream(data)
  // Go through each section of the form data and each key/value pair detecting what it is
  // and writing out the appropriate text.
  For Each d as DictionaryEntry in FormData
    out.WriteText("--" + Boundary + CRLF, TextEncoding.ASCII)
    Dim info As Xojo.Introspection.TypeInfo
    info = Xojo.Introspection.GetType(d.value)
    If  Info.Name.IndexOf("Text") <> -1 Then
      out.WriteText("Content-Disposition: form-data; name=""" + d.key + """" + CRLF + CRLF, TextEncoding.ASCII)
      out.WriteText(d.value + CRLF, Xojo.Core.TextEncoding.ASCII)
    ElseIf d.Value IsA FolderItem Then
      Dim file As FolderItem = d.Value
      out.WriteText("Content-Disposition: form-data; name=""" + d.key + """; filename=""" + File.Name + """" + CRLF, TextEncoding.ASCII)
      out.WriteText("Content-Type: "+contenttype+ CRLF + CRLF, TextEncoding.ASCII) 
      Dim bs As BinaryStream = BinaryStream.Open(File,BInaryStream.LockModes.Read)
      out.WriteText(CRLF, Xojo.Core.TextEncoding.ASCII)
    ElseIF d.value IsA MemoryBlock Then
      Dim m as MemoryBlock = d.Value
      Dim t as Text ="Content-Disposition: form-data; name=""" + d.key + """; filename=""blob""" 
      out.WriteText("Content-Disposition: form-data; name=""" + d.key + """; filename=""blob""" + CRLF, Xojo.Core.TextEncoding.ASCII)
      out.WriteText("Content-Type: "+contenttype+ CRLF + CRLF,TextEncoding.ASCII) 
      out.WriteText (CRLF,TextEncoding.ASCII)
    End If
  out.WriteText("--" + Boundary + "--" + CRLF,TextEncoding.ASCII)
  sock.SetRequestContent(data, "multipart/form-data; boundary=" + Boundary)

End Sub

Thank you so much for posting this. I am sure to need this and have saved this off to refer to in the future. Most of the other language web frameworks I have used have had wrappers to take care of this functionality, and I was assuming Xojo did as well.

This will save hours of head scratching. Much thanks.