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
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.
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;