TidHTTPServer decode content type multipart/form-data
Come tutti sappiamo, creare un webServer con Delphi è semplicissimo, cosa meno banale è invece decodificare una post che che contiene un multipart/form-data.
Quando una paginetta http esegue una http submit da un form in metodo post, magari con un file in upload il browser genera uno stream codificato in UTF8 in cui ci sono tutti i parametri e tutti gli allegati.
Il nostro server HTTP riceve tale stream nella variabile ARequestInfo.PostStream nell’evento “onCommandGet” (non bisogna farsi forviare dal nome, qualsiasi sia il metodo http utilizzato questo evento viene scatenato lato server).
Ora come maneggiare questa informazione è tutt’altro che banale, o si parsa lo stream tenendo conto degli standard RFC.
Personalmente ho utilizzato una classe già presente in Delphi nella tool dei componenti Indy che è nata per parsare stream molto simili : quelli MIME dei mesaggi di posta (son la stessa cosa alla fine).
Di seguito un esempio, il codice del comando onCommandGet l’ho implementato che restituisce una paginetta Http con un form per uploadare un file se viene chiamata la base path, quando invece viene chiamato basepath/upload gestisce la post del suddetto form.
L’esempio che riporto di seguito, salva lo stream con il nome file che gli arriva dal browser su un file nella cartella dell’applicazione. Ovviamente questo è un codice di esempio, se dovete implementare il webserver per un uso professionale bisogna considerare che è multi thread quindi le operazioni andrebbero sincronizzate con il thread principale, se non lo fate rischiate che si inchiodi prima o poi.
Tools : Delphi XE5 e Indy 10
E ora il codice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
interface uses System.SysUtils, System.Classes, Web.Win.Sockets, IdBaseComponent, IdComponent, IdCustomTCPServer, IdCustomHTTPServer, IdHTTPServer, IdContext, IdTCPServer,System.Generics.Collections, Data.DB, Datasnap.DBClient,IdHeaderList,idGlobal, IdIntercept,IdMessage,IdMessageCoderMIME,IdMessageCoder,IdGlobalProtocols; ... procedure TdmNet.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); Var ms : TMemoryStream; newdecoder, Decoder: TIdMessageDecoder; boundary, startboundary : String; msgEnd : Boolean; tmp : String; I : Integer; fname : String; tsValues : TStringList; begin i := 0; if pos('upload',lowercase(ARequestInfo.Document)) > 0 then begin If ARequestInfo.PostStream = nil then AResponseInfo.ContentText := '<HTML><BODY>unparsed:' + ARequestInfo.UnparsedParams + '<br>Encoding:' + ARequestInfo.ContentEncoding + ARequestInfo.RawHeaders.Values['Content-Type'] + '<br>HashCode:' + IntToStr(ARequestInfo.GetHashCode) + '<br>Params:' + ARequestInfo.Params.Text + ' -->stream nullo<br></BODY><HTML>' Else ARequestInfo.PostStream.Position := 0; msgEnd := False; boundary := ExtractHeaderSubItem(ARequestInfo.ContentType, 'boundary',QuoteHTTP); startboundary := '--' + boundary; repeat tmp := ReadLnFromStream(ARequestInfo.PostStream, -1, True); until tmp = startboundary; decoder := TIdMessageDecoderMIME.Create(nil); TIdMessageDecoderMIME(decoder).MIMEBoundary := boundary; tsValues := TStringList.Create; try repeat decoder.SourceStream := ARequestInfo.PostStream; decoder.FreeSourceStream := false; decoder.ReadHeader; inc(I); case Decoder.PartType of mcptAttachment,mcptText : begin ms := TMemoryStream.Create; ms.Position := 0; newdecoder := Decoder.ReadBody(ms,msgEnd); tmp := Decoder.Headers.Text; fname := decoder.Filename; decoder.Free; decoder := newdecoder; if decoder <> nil then TIdMessageDecoderMIME(decoder).MIMEBoundary := boundary; sleep(100); if fname <> '' then begin ms.SaveToFile(fname); //msgEnd := true; end else begin ms.SaveToFile(inttostr(i) + '.txt'); end; ms.Free; end; mcptIgnore: Begin try FreeAndNil(decoder); decoder := TIdMessageDecoderMIME.Create(nil); TIdMessageDecoderMIME(decoder).MIMEBoundary := boundary; finally ms.Free; end; End; mcptEOF: begin FreeAndNil(decoder); msgEnd := True end; end; until (decoder = nil) or(msgEnd); finally if decoder <> nil then decoder.Free; end; AResponseInfo.ContentText := AResponseInfo.ContentText + '</BODY><HTML>'; AResponseInfo.ContentText := '<HTML><BODY>unparsed:' + ARequestInfo.UnparsedParams + '<br>Encoding:' + ARequestInfo.ContentEncoding + '<br>Conte' + ARequestInfo.RawHeaders.Values['Content-Type'] + '<br>HashCode:' + IntToStr(ARequestInfo.GetHashCode) + '<br>Params:' + ARequestInfo.Params.Text + ' -->stream nullo<br></BODY><HTML>'; end Else Begin AResponseInfo.ContentText := '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ' + #10#13 + '<html xmlns="http://www.w3.org/1999/xhtml"> ' + #10#13 + '<head> ' + #10#13 + '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> ' + #10#13 + '<title>Pagina di prova</title> ' + #10#13 + '</head> ' + #10#13 + ' ' + #10#13 + '<body> ' + #10#13 + '<h1>Test multipart from <a href="www.synaptica.info">Synaptica Srl</a> </h1> <BR><BR> ' + #10#13 + '<form action="upload" method="post" enctype="multipart/form-data"> ' + #10#13 + '<p>key ' + #10#13 + ' <input type="text" name="key" id="key" /> ' + #10#13 + '</p> ' + #10#13 + '<p>group ' + #10#13 + ' <input type="text" name="group" id="key" /> ' + #10#13 + '</p> ' + #10#13 + ' ' + #10#13 + '<label for="file">Filename:</label> ' + #10#13 + '<label for="file">' + ARequestInfo.Document + '</label> ' + #10#13 + '<input type="file" name="file" id="file" /> ' + #10#13 + '<br /> ' + #10#13 + '<input type="submit" name="submit" value="Submit" /> ' + #10#13 + '</form></p> ' + #10#13 + '</body> ' + #10#13 + '</html> '; End; end; |