Garbage in headers when POST-ing with Indy 10.5.8

前端 未结 2 1898
予麋鹿
予麋鹿 2021-01-21 20:13

I\'m trying to send a file using POST in multipart/form data via Indy 10.5.8. I\'m using Delphi XE2 and I\'ve been trying to POST a file to a server. This is the firs time I\'ve

相关标签:
2条回答
  • 2021-01-21 20:35

    Do all of those . characters represent a 00 byte? Because that looks like the result of a ASCII->UTF16 conversion. And the garbage in the Content-Disposition could be related to a conversion like that copying bytes around and getting the end of the buffer wrong, leaving you with a corrupted string.

    If you can come up with code to reproduce this consistently, you should probably post a bug report on the Indy forums.

    0 讨论(0)
  • 2021-01-21 20:48

    The root of the problem is that your custom TStream code is not compatible with D2009+ versions of Delphi. Delphi's String and PChar types are not Ansi anymore, but the code assumes they still are. They are Unicode UTF-16 now. You are not accounting for that correctly, eg:

    procedure TMsMultiPartFormDataStream.AddFile(const FieldName, FileName, ContentType: string; FileData: TStream);   
    var   
      sFormFieldInfo: AnsiString;
      iSize: Int64;   
    begin   
      iSize := FileData.Size;   
      // NOTE: this will only work for ASCII filenames!!!!
      //
      // Non-ASCII filenames will get converted to Ansi, which can cause data loss.
      // To send non-ASCII filenames correctly, you have to encode it to a charset
      // first, such as UTF-8, and then encode the resulting bytes using
      // MIME's RFC2047 encoding so the server can decode the filename properly
      // on its end...
      //
      sFormFieldInfo := Format(CRLF + '--' + Boundary + CRLF + CONTENT_DISPOSITION +   
        FILE_NAME_PLACE_HOLDER + CRLF + CONTENT_LENGTH +   
          CONTENT_TYPE_PLACE_HOLDER, [FieldName, FileName, iSize, ContentType]);   
      {so: boundary + crlf + content-disposition+file-name-place-holder}   
    
      Write(sFormFieldInfo[1], Length(sFormFieldInfo) * SizeOf(AnsiChar));   
    
      if iSize > 0 then
      begin
        FileData.Position := 0;   
        CopyFrom(FileData, iSize);   
      end;
    end;   
    
    procedure TMsMultiPartFormDataStream.AddFormField(const FieldName, FieldValue: string);   
    var   
      sFormFieldInfo: AnsiString;   
    begin   
      // NOTE: this will only work for ASCII text!!!!
      //
      // Non-ASCII text will get converted to Ansi, which can cause data loss.
      // To send non-ASCII text correctly, you have to encode it to a charset
      // first, such as UTF-8 and then encode the resulting bytes using
      // MIME's 'quoted-printable' or 'base64' enoding, and then include
      // appropriate 'charset' and Content-Transfer-Encoding' headers so the
      // server can decode the data properly on its end...
      //
      sFormFieldInfo := Format(CRLF + '--' + Boundary + CRLF + CONTENT_DISPOSITION + CRLF + CRLF +   
        FieldValue, [FieldName]);   
      Write(sFormFieldInfo[1], Length(sFormFieldInfo) * AnsiString(AnsiChar));   
    end;   
    
    procedure TMsMultiPartFormDataStream.PrepareStreamForDispatch;   
    var   
      sFormFieldInfo: AnsiString;   
    begin   
      sFormFieldInfo := CRLF + '--' + Boundary + '--' + CRLF;   
      Write(sFormFieldInfo[1], Length(sFormFieldInfo) * SizeOf(AnsiChar));   
      Position := 0;   
    end;   
    

    With that said, I strongly suggest you get rid of your custom TMsMultiPartFormDataStream class completely. All it is doing is mimicing an outdated version of Indy's own TIdMultipartFormDataStream class . Just use Indy's native TIdMultipartFormDataStream class as-is instead. It handles D2009+ Unicode for you, eg:

    uses
      ..., IdMultipartFormData;
    
    function PostFile(const filename, apikey: string): boolean; 
    var 
      ResponseStream: TMemoryStream; 
      MultiPartFormDataStream: TIdMultiPartFormDataStream; 
    begin 
      Result := False; 
    
      //Form5.IdHTTP1.HandleRedirects := true; 
      Form5.idHTTP1.ReadTimeout := 0; 
      //Form5.idHTTP1.IOHandler.LargeStream := True; 
    
      try
        ResponseStream := TMemoryStream.Create; 
        try
          MultiPartFormDataStream := TIdMultiPartFormDataStream.Create; 
          try 
            MultiPartFormDataStream.AddFormField('apikey', apikey); 
            MultiPartFormDataStream.AddFile('file', filename, 'application/octet-stream');      
            Form5.IdHTTP1.Post('http://www.updserver.tld/api/file/save', MultiPartFormDataStream, ResponseStream); 
            ResponseStream.SaveToFile(ExtractFilePath(Application.ExeName) + 'a.txt'); 
            Result := True; 
          finally 
            MultiPartFormDataStream.Free; 
          end;
        finally
          ResponseStream.Free; 
        end; 
      except 
        on E:Exception do 
        begin 
          Form5.Close; 
          ShowMessage('Upload failed! ' + E.Message); 
          end; 
        end; 
      end; 
    end;
    
    0 讨论(0)
提交回复
热议问题