AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Delphi multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr
Thema durchsuchen
Ansicht
Themen-Optionen

multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

Ein Thema von ConstantGardener · begonnen am 20. Nov 2014 · letzter Beitrag vom 6. Dez 2014
Antwort Antwort
ConstantGardener

Registriert seit: 24. Jan 2006
Ort: Halberstadt
376 Beiträge
 
Delphi 10.4 Sydney
 
#1

multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 20. Nov 2014, 22:18
Hallo Leute,

ich habe hier bei der Umstellung von XE6 auf XE7 ein Problem beim Upload von JPEG-Dateien aus einem HTML5-Form auf meinen INDY-HTTP Server.

Der Upload an sich funktioniert, die resultierende Datei ist aber defekt (JPEG Error #53).
Ich nutze hier ein Code Beispiel von Remy Lebeau zum dekodieren der Part's des MIME-Blocks der unter XE6 perfekt funktioniert.
An der Indy-Version sollte es nicht liegen da ich in beiden Fällen gegen die aktuelle INDY Version (18.11.14) compiliere.

Ich vermute ein Problem in einem geänderten Stream-Verhalten. Hat irgendwer von euch hier Veränderungen bemerkt und kann mir Hinweise geben wie ich das alte Verhalten wieder herstelle?



Danke im Voraus.


ConstantGardener
Andreas Schachtner

Geändert von ConstantGardener (21. Nov 2014 um 06:25 Uhr)
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#2

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 22. Nov 2014, 11:25
Ab Indy Revision 5128 vom 9. April 2014 sind die dekodierten Uploads um zwei Zeichen länger als die Originaldatei.

Die zusätzlichen Bytes sind (im Test mit einer Textdatei) ein CR/LF am Dateiende.

Mit Revision 5127 (auch vom 8. April 2014) stimmen die hochgeladenen Dateien mit den Originalen überein.

Im Log steht dass ab Revision 5128 das Default-Encoding 7bit für empfangene Multipart-Daten geht, aber warum diese Änderung nur zum Anhängen eins CR/LF führte konnte ich leider nicht erkennen:

Zitat:
Tweaks to MIME Content-Transfer-Encoding handling. Now defaulting to 7bit when Content-Transfer-Encoding is not present, per RFC 2045.
Ich schlage vor, es einmal mit Revision 5128 zu versuchen um zu bestätigen dass es das gleiche Problem sein könnte. Ich teste nur mit Delphi 2009 und den Unterschied zwischen XE6 und XE7 daher nicht untersuchen.

Hier ist der Server-Code der auf http://127.0.0.1:8080 ein Upload-Formular liefert:


Delphi-Quellcode:
program IndyMultipartUploadDemo;

{$APPTYPE CONSOLE}

uses
  IdHTTPServer, IdCustomHTTPServer, IdContext, IdSocketHandle, IdGlobal,
  IdMessageCoder, IdGlobalProtocols, IdMessageCoderMIME, IdMultiPartFormData,
  SysUtils, Classes;

type
  TMimeHandler = procedure(var VDecoder: TIdMessageDecoder;
    var VMsgEnd: Boolean; const Response: TIdHTTPResponseInfo) of object;

  TMyServer = class(TIdHTTPServer)
  private
    procedure ProcessMimePart(var VDecoder: TIdMessageDecoder;
      var VMsgEnd: Boolean; const Response: TIdHTTPResponseInfo);
    function IsHeaderMediaType(const AHeaderLine, AMediaType: String): Boolean;
    function MediaTypeMatches(const AValue, AMediaType: String): Boolean;
    function GetUploadFolder: string;
    procedure HandleMultipartUpload(Request: TIdHTTPRequestInfo; Response:
     TIdHTTPResponseInfo; MimeHandler: TMimeHandler);
  public
    procedure InitComponent; override;
    procedure DoCommandGet(AContext: TIdContext;
      ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); override;
  end;

procedure Demo;
var
  Server: TMyServer;
begin
  ReportMemoryLeaksOnShutdown := True;

  Server := TMyServer.Create;
  try
    try
      Server.Active := True;
    except
      on E: Exception do
      begin
        WriteLn(E.ClassName + ' ' + E.Message);
      end;
    end;
    WriteLn('Hit any key to terminate.');
    ReadLn;
  finally
    Server.Free;
  end;
end;

procedure TMyServer.InitComponent;
var
  Binding: TIdSocketHandle;
begin
  inherited;

  Bindings.Clear;
  Binding := Bindings.Add;
  Binding.IP := '127.0.0.1';
  Binding.Port := 8080;

  KeepAlive := True;
end;

procedure TMyServer.DoCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  AResponseInfo.ContentType := 'text/html';
  AResponseInfo.CharSet := 'UTF-8';

  if ARequestInfo.CommandType = hcGET then
  begin
    AResponseInfo.ContentText :=
        '<!DOCTYPE HTML>' + #13#10
      + '<html>' + #13#10
      + ' <head>' + #13#10
      + ' <title>Multipart Upload Example</title>' + #13#10
      + ' </head>' + #13#10
      + ' <body> ' + #13#10
      + ' <form enctype="multipart/form-data" method="post">' + #13#10
      + ' <fieldset>' + #13#10
      + ' <legend>Standard file upload</legend>' + #13#10
      + ' <label>File input</label>' + #13#10
      + ' <input type="file" class="input-file" name="upload" />' + #13#10
      + ' <button type="submit" class="btn btn-default">Upload</button>' + #13#10
      + ' </fieldset>' + #13#10
      + ' </form>' + #13#10
      + ' </body>' + #13#10
      + '</html>' + #13#10;
  end
  else
  begin
    if ARequestInfo.CommandType = hcPOST then
    begin
      if IsHeaderMediaType(ARequestInfo.ContentType, 'multipart/form-data') then
      begin
        HandleMultipartUpload(ARequestInfo, AResponseInfo, ProcessMimePart);
      end;
    end;
  end;
end;

// based on code on the Indy and Winsock Forum articles
// http://forums2.atozed.com/viewtopic.php?f=7&t=10924
// http://embarcadero.newsgroups.archived.at/public.delphi.internet.winsock/201107/1107276163.html

procedure TMyServer.ProcessMimePart(var VDecoder: TIdMessageDecoder;
  var VMsgEnd: Boolean; const Response: TIdHTTPResponseInfo);
var
  LMStream: TMemoryStream;
  LNewDecoder: TIdMessageDecoder;
  UploadFile: string;
begin
  LMStream := TMemoryStream.Create;
  try
    LNewDecoder := VDecoder.ReadBody(LMStream, VMsgEnd);
    if VDecoder.Filename <> 'then
    begin
      try
        LMStream.Position := 0;
        Response.ContentText := Response.ContentText
          + Format('<p>%s %d bytes</p>' + #13#10,
            [VDecoder.Filename, LMStream.Size]);

        // write stream to upload folder
        UploadFile := GetUploadFolder + VDecoder.Filename;
        LMStream.SaveToFile(UploadFile);
        Response.ContentText := Response.ContentText
          + '<p>' + UploadFile + ' written</p>';

      except
        LNewDecoder.Free;
        raise;
      end;
    end;
    VDecoder.Free;
    VDecoder := LNewDecoder;
  finally
    LMStream.Free;
  end;
end;

function TMyServer.IsHeaderMediaType(const AHeaderLine, AMediaType: String): Boolean;
begin
  Result := MediaTypeMatches(ExtractHeaderItem(AHeaderLine), AMediaType);
end;

function TMyServer.MediaTypeMatches(const AValue, AMediaType: String): Boolean;
begin
  if Pos('/', AMediaType) > 0 then begin
    Result := TextIsSame(AValue, AMediaType);
  end else begin
    Result := TextStartsWith(AValue, AMediaType + '/');
  end;
end;

function TMyServer.GetUploadFolder: string;
begin
  Result := ExtractFilePath(ParamStr(0)) + 'upload\';
  ForceDirectories(Result);
end;

procedure TMyServer.HandleMultipartUpload(Request: TIdHTTPRequestInfo;
  Response: TIdHTTPResponseInfo; MimeHandler: TMimeHandler);
var
  LBoundary, LBoundaryStart, LBoundaryEnd: string;
  LDecoder: TIdMessageDecoder;
  LLine: string;
  LBoundaryFound, LIsStartBoundary, LMsgEnd: Boolean;
begin
  LBoundary := ExtractHeaderSubItem(Request.ContentType, 'boundary',
    QuoteHTTP);
  if LBoundary = 'then
  begin
    Response.ResponseNo := 400;
    Response.CloseConnection := True;
    Response.WriteHeader;
    Exit;
  end;

  LBoundaryStart := '--' + LBoundary;
  LBoundaryEnd := LBoundaryStart + '--';

  LDecoder := TIdMessageDecoderMIME.Create(nil);
  try
    TIdMessageDecoderMIME(LDecoder).MIMEBoundary := LBoundary;
    LDecoder.SourceStream := Request.PostStream;
    LDecoder.FreeSourceStream := False;

    LBoundaryFound := False;
    LIsStartBoundary := False;
    repeat
      LLine := ReadLnFromStream(Request.PostStream, -1, True);
      if LLine = LBoundaryStart then
      begin
        LBoundaryFound := True;
        LIsStartBoundary := True;
      end
      else if LLine = LBoundaryEnd then
      begin
        LBoundaryFound := True;
      end;
    until LBoundaryFound;

    if (not LBoundaryFound) or (not LIsStartBoundary) then
    begin
      Response.ResponseNo := 400;
      Response.CloseConnection := True;
      Response.WriteHeader;
      Exit;
    end;

    LMsgEnd := False;
    repeat
      TIdMessageDecoderMIME(LDecoder).MIMEBoundary := LBoundary;
      LDecoder.SourceStream := Request.PostStream;
      LDecoder.FreeSourceStream := False;

      LDecoder.ReadHeader;
      case LDecoder.PartType of
        mcptText, mcptAttachment:
          begin
            MimeHandler(LDecoder, LMsgEnd, Response);
          end;
        mcptIgnore:
          begin
            LDecoder.Free;
            LDecoder := TIdMessageDecoderMIME.Create(nil);
          end;
        mcptEOF:
          begin
            LDecoder.Free;
            LMsgEnd := True;
          end;
      end;
    until (LDecoder = nil) or LMsgEnd;
  finally
    LDecoder.Free;
  end;
end;

begin
  Demo;
end.
Michael Justin

Geändert von mjustin (22. Nov 2014 um 11:41 Uhr)
  Mit Zitat antworten Zitat
ConstantGardener

Registriert seit: 24. Jan 2006
Ort: Halberstadt
376 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 22. Nov 2014, 12:21
Hallo Herr Justin,

erstmal danke für die Mühe. Das geht bei Ihnen natürlich einfach schneller mit der Testversion.

Nun zu meinen bisherigen Erkenntnissen:

1) Es funktioniert und liefert valide JPEG-Dateien mit XE6 und der originalen (sprich vorinstallierten) INDY-Version
2) Die JPEG Dateien werden falsch übertragen mit einer aktuellen INDY-Version (vom 8.11.2014) und XE6
3) Verhalten bei XE7 mit der originalen und der aktuellen INDY-Version --> zerstörte JPG's

Fazit : Es wird doch an der Indy-Version liegen. Ich teste weiter.
Andreas Schachtner
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#4

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 2. Dez 2014, 20:44
Crosspost auf http://stackoverflow.com/questions/27257577/
Michael Justin
  Mit Zitat antworten Zitat
ConstantGardener

Registriert seit: 24. Jan 2006
Ort: Halberstadt
376 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 2. Dez 2014, 21:29
...vielen Dank für die Mühe!!
Andreas Schachtner
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#6

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 4. Dez 2014, 08:02
Indy-Entwickler Remy Lebeau hat die Frage beantwortet, die Hintergründe ausführlich beschrieben, einen Workaround (Patch) für Indy TIdMessageDecoderMIME.ReadBody() veröffentlicht, und den Bug in den Indy Bug Tracker aufgenommen.




p.s. mit einem HTML5 fähigen Browser kann multipart/form-data in einem einzigen POST auch mehrere Dateien hochladen, der Dateidialog erlaubt dann eine Mehrfachauswahl wenn das 'multiple' Attribut des file input Formularfelds gesetzt ist.
Michael Justin

Geändert von mjustin ( 4. Dez 2014 um 08:06 Uhr)
  Mit Zitat antworten Zitat
ConstantGardener

Registriert seit: 24. Jan 2006
Ort: Halberstadt
376 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 4. Dez 2014, 08:41
Hallo Herr Justin,

ja mit dem Hochladen mehrerer Dateien ist klar. Was ganz oben auf meiner Wunschliste steht ist aber eher das übertragen von Texten(Textarea mit Bildbeschreibung) und Files (Bilddateien) zum meinem Indy/WebComponents-Server in einem Poststream. Das funktioniert ja leider bisher nicht da die Erkennung der MimeParts nicht wirklich funktioniert.
Andreas Schachtner
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#8

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 4. Dez 2014, 09:04
Hallo Herr Justin,

ja mit dem Hochladen mehrerer Dateien ist klar. Was ganz oben auf meiner Wunschliste steht ist aber eher das übertragen von Texten(Textarea mit Bildbeschreibung) und Files (Bilddateien) zum meinem Indy/WebComponents-Server in einem Poststream. Das funktioniert ja leider bisher nicht da die Erkennung der MimeParts nicht wirklich funktioniert.
Stimmt, das war noch ein Punkt auf der Wunschliste zum Upload, ich versuche dazu ein Beispielprojekt auf Stackoverflow zu veröffentlichen, Remy Lebau hat dazu sicher eine Antwort und eventuell eine Lösung.
Michael Justin
  Mit Zitat antworten Zitat
ConstantGardener

Registriert seit: 24. Jan 2006
Ort: Halberstadt
376 Beiträge
 
Delphi 10.4 Sydney
 
#9

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 4. Dez 2014, 13:11
Perfekt!!!
Andreas Schachtner
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#10

AW: multipart/form-data Datei-Upload funktioniert unter XE7 nicht mehr

  Alt 6. Dez 2014, 10:18
Update: Remy Lebeau hat den Bug in Indy gefixt
Michael Justin
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:50 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz