billyb 1 Posted April 2 I have need to send files to a Delphi service. I am able to send the files, but have not figured out how to extract the multipart items when received. procedure TForm15.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); begin ARequestInfo.PostStream.Seek(0, soFromBeginning); Memo1.Lines.LoadFromStream(ARequestInfo.PostStream); end; Here is what I get when file is posted with a 2 other values added to the stream. What I need is a way to access each item. How do I ask for "type" and get back "OPRS" How do I ask for "ID" and get back "1234" How do I ask for "filename" and get back "test.pdf" How do I ask for actual file contents and save to memory stream to then write to disk ----------040224182640701 Content-Disposition: form-data; name="type" Content-Type: text/plain Content-Transfer-Encoding: quoted-printable OPRS ----------040224182640701 Content-Disposition: form-data; name="ID" Content-Type: text/plain Content-Transfer-Encoding: quoted-printable 1234 ----------040224182640701 Content-Disposition: form-data; name="file"; filename="test.PDF" Content-Type: application/pdf Content-Transfer-Encoding: binary %PDF-1.6 %âãÏÓ 290 0 obj << /Border [ 0 0 0 ] /Contents (þÿ Share this post Link to post
Remy Lebeau 1396 Posted April 2 (edited) TIdHTTPServer does not currently support extracting values from a "multipart/form-data" request (see https://github.com/IndySockets/Indy/issues/138). You have to parse the raw MIME data in the PostStream yourself. You can use Indy's TIdMessageDecoderMIME class to help you with that. See https://en.delphipraxis.net/topic/10918-multipartform-data-vs-x-www-form-urlencoded-indy-http-server/ for an example. Edited April 3 by Remy Lebeau Share this post Link to post
billyb 1 Posted April 3 I added your code and the section where you create the DEST stream appears is not containing the value If I add sl:=tstringlist.Create; sl.LoadFromStream(dest); my stringlist is empty What would be great is a function you can call that returns a value for a name example GetPostStreamValue(aname:string,var avalue:String;,var astream):boolean where aname is the item name (Like "type") avalue = the value for the name (like "OPRS") astream = a memory stream you pass in that gets filled with the file contents when Content-Transfer-Encoding: binary case Decoder.PartType of mcptText, mcptAttachment: begin Dest := TMemoryStream.Create; try NewDecoder := Decoder.ReadBody(Dest, MsgEnd); try // use Dest as needed... sl:=tstringlist.Create; sl.LoadFromStream(dest); finally Decoder.Free; Decoder := NewDecoder; end; finally Dest.Free; end; end; Share this post Link to post
billyb 1 Posted April 3 I found the issue with the empty stringlist. I forgot to do a seek(0,0). This is a lot more complicated then I expected. It appears I have to manually parse the dest stream to get the name and the contents as it contains both. Content-Disposition: form-data; name="file"; filename="test.PDF" Content-Type: application/pdf Content-Transfer-Encoding: binary %PDF-1.6 %âãÏÓ I saw that Remy posted a enhancement request back in 2017 to add mutlipart parsing. Sad it was not added. Bill B Share this post Link to post
Remy Lebeau 1396 Posted April 3 (edited) 3 hours ago, billyb said: What would be great is a function you can call that returns a value for a name example GetPostStreamValue(aname:string,var avalue:String;,var astream):boolean where aname is the item name (Like "type") avalue = the value for the name (like "OPRS") astream = a memory stream you pass in that gets filled with the file contents when Content-Transfer-Encoding: binary That's not going to happen anytime soon, if ever. You will have to write your own function if you want that kind of functionality. 3 hours ago, billyb said: This is a lot more complicated then I expected. MIME is complicated. Quote It appears I have to manually parse the dest stream to get the name and the contents as it contains both. No, the Dest stream only contains the body content (that is why the method is called ReadBody()). The Decoder has a Headers property, which is populated by the call to the ReadHeaders() method. The headers are stored as a TStrings in "name=value" format. You can use Indy's ExtractHeaderSubItem() function to get the "name" and "filename" attribute values from the "Content-Disposition" header. Edited April 3 by Remy Lebeau Share this post Link to post