TIdHTTPServer file upload
Asked Answered
M

5

6

I'm trying to upload files to a Indy(ver. 10.5.5) TIdHTTPServer.
I've been searching for solutions but no luck thus far, what I've found was for older versions of Indy which were not compatible with the version shipped with Delphi 2010.

What I'm hoping is to achieve simply upload a file using "multipart/form-data" to the server and decode it, simple as that, any help is appreciated.

Mckelvey answered 31/5, 2011 at 17:44 Comment(4)
For the MIME decoding part, Indy contains a TIdDecoderMIME class in unit Protocols\IdCoderMIMEGuyette
yes, but it doesn't handle the data as I need it to unfortunately... thank you for your commentMckelvey
10.5.5 is not very old, but I'd try the current Indy 10.5.8 versionGuyette
true, but unfortunately I need to make sure that anyone with a clean install of Delphi 2010 can simply build the project without needing to update indy or any other component... requirement.Mckelvey
A
4

I started xxm as a way to build websites with Delphi, and have scripts with both HTML and Pascal code re-compile with a press of the browser's refresh button after changes made.

It uses a generic interface that 'plugs into' IIS, Apache, Internet Explorer, FireFox, and there's a stand-alone HTTP exe also. The interface exposes IxxmParameterPostFile on a parameter when a file is uploaded.

See demo 4 Upload for an example.

Alkyd answered 1/6, 2011 at 20:1 Comment(3)
it's not really on the question but pretty much on the spot, very nice project, I can't believe I haven't seen it before. Any chance it will be ported to "Free Pascal" for cross platform support? I haven't got enough time to "play" at the moment, but I will sure give XXM more attention in the next couple of weeks.Mckelvey
Porting to FreePascal hinges on a decent ComServ.pas, which was missing every last time I checked. I could have a stab at creating an open-source ComServ.pas, if I had all the time and money of the world...Alkyd
I feel you Stijn, even tho' I can't switch to XXM right now, it is the closest option to my question, thank you!Mckelvey
B
8

TIdHTTPServer does not currently support multipart/form-data submissions natively. That is on the todo list for Indy 11. In the meantime, you have to parse the posted MIME data manually using TIdDecoderMIME, as mjn suggested. There have been examples of that posted in the Embarcadero and Indy forums before.

Badminton answered 1/6, 2011 at 6:30 Comment(6)
Hi Remy, I've managed to find that out... the demos available are not parsing the data correctly, it leaves the end boundary in the file... as a temporary solution in the "OnCreatePostStream" event of the server I simply create a file stream and on the "OnCommandGet" event I just skip the boundary and copy the correct data in the "final file stream" and it seems to be working as I need at least for the moment...Mckelvey
If a boundary is being left behind, then you are not processing the data correctly. Which demos are you looking at exactly?Badminton
forums2.atozed.com/viewtopic.php?f=7&t=23732 the second post is yours I believe(gambit47?), mcptEOF does not exist in Indy 10.5.5, but I still tried to upload, sometimes(when big files were uploaded, hundreds of megs ZIP or RAR files) boundary was removed, in other cases like PDF's the boundary was left, I believe it has something to do with the CR, LF, CRLF combination at some point when reading the stream content. Currently, "OnCreatePostStream" I create a file stream, and "OnCommandGet" I use ReadLnFromStream to bypass the first 4 lines(boundary and file info) and read until...Mckelvey
the end of the file MINUS (2 bytes + length of the boundary "------MARKER--") and it works, however I would like a more elegant solution that this if possible.Mckelvey
Yes, I am gambit47 on that forum. mcptIgnore and mcptEOF were introduced to address issues with parsing nested MIME parts. 10.5.5 is an old version, the current version is 10.5.8 (and there have been a lot of internal changes since 10.5.5), so you should really consider upgrading. For Indy 11, Indy's MIME parsing code is going to be refactored and generalized so it can be re-used more easily. Right now, it is primarily designed for email parsing, and it takes a lot of manual work (but can be done) to use it in other situations, like HTTP uploads.Badminton
Thank you Remy, I will have to wait before upgrading... there are some constraints to which I must obey, hopefully in XE3 the new version of Indy will do my job (:Mckelvey
A
4

I started xxm as a way to build websites with Delphi, and have scripts with both HTML and Pascal code re-compile with a press of the browser's refresh button after changes made.

It uses a generic interface that 'plugs into' IIS, Apache, Internet Explorer, FireFox, and there's a stand-alone HTTP exe also. The interface exposes IxxmParameterPostFile on a parameter when a file is uploaded.

See demo 4 Upload for an example.

Alkyd answered 1/6, 2011 at 20:1 Comment(3)
it's not really on the question but pretty much on the spot, very nice project, I can't believe I haven't seen it before. Any chance it will be ported to "Free Pascal" for cross platform support? I haven't got enough time to "play" at the moment, but I will sure give XXM more attention in the next couple of weeks.Mckelvey
Porting to FreePascal hinges on a decent ComServ.pas, which was missing every last time I checked. I could have a stab at creating an open-source ComServ.pas, if I had all the time and money of the world...Alkyd
I feel you Stijn, even tho' I can't switch to XXM right now, it is the closest option to my question, thank you!Mckelvey
B
3

Only suitable solution I can find without personally testing (let me know if this doesn't lead you to a working solution for your needs, and I'll fire up XE and produce something more eloquent)

Bigley answered 31/5, 2011 at 17:57 Comment(5)
just an FYI, the active part of that is creating a TStream (TFileStream or TMemoryStream), then calling IdHTTP1.Post(<url>, <StreamVarName>);Bigley
it's not exactly what I need, see the problem is that the data being sent by the browser also needs to be decoded from MIME to binary.Mckelvey
ah... might be worth adjusting your question to clarify that requirement :)Bigley
sorry, I'm a bit frustrated by the fact that I wasn't able to achieve this relatively simple task by my own... question updated.Mckelvey
Well, we can't always solve every problem by ourselves... otherwise StackOverflow wouldn't exist, would it? :PBigley
D
1

http://www.synaptica.info/2014/01/31/tidhttpserver-decode-content-type-multipartform-data/

I do a simple multipart upload like this:

used units:

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;

the code that loop on mime content:

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;
Dilorenzo answered 4/11, 2015 at 9:47 Comment(0)
D
1

It seems to me that this is a perennial problem. I have looked at all sorts of proposals on the internet and there is nothing comparable to MsMultipartparser.pas on offer. Sadly, it does not work for Unicode.

Why does not someone a lot smarter than me rewrite MsMultipartparser.pas and save us all an awful lot of trouble?

Indy - does not work.

IPWorks - seems to just give you filename(s) and so on. Can't work out how to get file itself.

Alcinoe - no installation instructions let alone explanation and does not seem to work for XE8 or Delphi 10

I looked at several others but they are all impractical.

Dunt answered 29/12, 2015 at 23:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.