如何在 TIdMultiPartFormDataStream 中包含一个文件以用于 Indy IdHTTP1.Post?
How do I include a file in a TIdMultiPartFormDataStream for use with Indy IdHTTP1.Post?
使用 Indy 的 idHTTP.post 发送一个或多个任何类型的文件以及其他参数时需要使用的正确代码是什么? (使用 Delphi 2009 和 Indy 10)
所讨论的 post 调用商业公司 API (ElasticEmail) 中的一个函数,该函数向参数之一中保存的收件人发送电子邮件。 (关于我正在调用的函数的文档的 link 是 here.
我有来自公司 here 的 C# 和其他语言的示例代码,我试图在下面的 Delphi 代码中复制该代码。
如果在过程 btnSendbyElastic 中,我注释掉行 Filenames.add(Afilename);
以便函数 Upload 不尝试附加文件,那么正确的调用似乎是在电子邮件成功发送时进行的API。
但是,如果我保留该行,以便函数 UpLoad
中的行
MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.Addfile('file'+inttostr(i), filenames[i],MIMEStr);
do 被执行,然后没有邮件被发送,服务器的响应是
{"success":false,"error":"One of files has invalid characters in file name."}
(文件 Afilename 确实存在于该位置,我已尝试使用单反斜杠和双反斜杠)
阅读关于此主题的其他 SO posts 我还尝试用以下循环替换 Function UpLoad 中的文件处理循环
for i := 0 to filenames.Count - 1 do
begin
MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.AddFile('file'+inttostr(i), filenames[i],MIMEStr);
AttachmentContent := TFileStream.Create(filenames[i],fmOpenRead);
try
FormData.AddFormField(AttachmentContent.ToString,filenames[i]);
finally
AttachmentContent.free;
end;
end;
这一次,即使使用 Filenames.add(Afilename);
中指定的文件名,电子邮件也能正确发送,但收件人看不到附件。
在许多其他问题中,我已经阅读了这些可能重复的 SO 问题
Nodejs POST request multipart/form-data
特别是
(这几乎正是我想要做的)但我仍然看不出我在代码中做错了什么以及我需要做些什么来纠正它。
这是我正在使用的代码(UPPER_CASE 标识符是在别处定义的常量)
PS 我在英国,很抱歉回复美国的时间延迟 comments/answers
function TForm1.Upload(url: string; params, filenames: Tstringlist): string;
var
FormData : TIdMultiPartFormDataStream;
MIMEStr, ResponseText : string;
i : integer;
begin
try
FormData := TIdMultiPartFormDataStream.Create;
for i := 0 to params.Count - 1 do
FormData.AddFormField(params.Names[i],params.values[params.Names[i]]);
for i := 0 to filenames.Count - 1 do
begin
MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.Addfile(filenames[i], filenames[i],MIMEStr);
end;
ResponseText :=IdHTTP1.Post(url, FormData);
Memo1.Text := ResponseText; //debug
finally
FormData.free;
end;
end;
procedure TForm1.btnSendbyElastic(Sender: TObject);
var
Params, Filenames : Tstringlist;
url, Afilename : string;
begin
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
Params := Tstringlist.Create;
Filenames := Tstringlist.Create;
try
Params.add('apikey=' + ELASTIC_MAIL_API_KEY) ;
Params.add('from=' + ELASTIC_EMAIL_FROM_EMAIL) ;
Params.add('fromname=' + ELASTIC_EMAIL_FROM_NAME) ;
Params.add('Subject=' + 'The Subject') ;
Params.add('bodyHtml=' + '<h1>Html Body</h1>') ;
Params.add('bodyText=' + 'Text Body') ;
Params.add('to=' + THE_RECIPIENT_ADDRESS) ;
Filenames.add(Afilename); //*** comment out this line and an email is sent correctly
url := ELASTIC_EMAIL_EMAIL_SEND ;
Upload (url , params, filenames );
finally
Params.free;
Filenames.free;
end;
函数GetMIMETypeFromFile
定义在Indy单元idGlobalProtocols中。我没有写它,我只是调用它。不过我已经按照要求转载在这里了
function GetMIMETypeFromFile(const AFile: TIdFileName): string;
var
MIMEMap: TIdMIMETable;
begin
MIMEMap := TIdMimeTable.Create(True);
try
Result := MIMEMap.GetFileMIMEType(AFile);
finally
MIMEMap.Free;
end;
end;
我发现您的代码存在一些问题。
您在文件路径中错误地转义了 \
个字符。这在 C 和 C++ 等语言中是必需的,但在 Delphi 中根本不需要,所以去掉它。
改变这个:
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
为此:
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
我看到的下一个问题是您在将文件附件字段添加到 TIdMultipartFormDataStream
时没有正确命名它们。
调用 AddFile()
时,您将按原样将完整的文件路径传递给 AFieldName
参数,而不是使用 file0
、file1
等名称如 Elastic 的示例所示。
改变这个:
FormData.Addfile(filenames[i], filenames[i],MIMEStr);
对此1:
FormData.AddFile('file'+IntToStr(i), filenames[i], MIMEStr);
1:仅供参考,无需手动调用 GetMIMETypeForFile()
,如果您不提供字符串,AddFile()
会在内部为您调用 GetMIMETypeForFile()
AContentType
参数,例如FormData.AddFile('file'+IntToStr(i), filenames[i]);
当您尝试使用 AddFormField()
而不是 AddFile()
添加附件时,您犯了类似的错误。您将每个文件的实际数据内容用于 AFieldName
参数,而不是将内容用于 AFieldValue
参数。
在这种情况下,更改为:
FormData.AddFormField(AttachmentContent.ToString,filenames[i]);
为此:
FormData.AddFormField('file'+IntToStr(i), AttachmentContent.ToString, '', MIMEStr, filenames[i]);
或者,由于您自己打开 TFileStream
对象,您可以使用重载的 AddFormField()
方法,该方法将 TStream
作为输入(只是确保不要释放 TStream
个对象,直到您使用完 TIdMultipartFormDataStream
!):
AttachmentContent := TFileStream.Create(filenames[i], fmOpenRead);
FormData.AddFormField('file'+IntToStr(i), MIMEStr, '', AttachmentContent, filenames[i]);
话虽如此,试试这样的东西:
function TForm1.Upload(url: string; params, filenames: TStrings): string;
var
FormData : TIdMultiPartFormDataStream;
ResponseText : string;
i : integer;
begin
FormData := TIdMultiPartFormDataStream.Create;
try
for i := 0 to params.Count - 1 do
FormData.AddFormField(params.Names[i], params.ValueFromIndex[i]);
for i := 0 to filenames.Count - 1 do
FormData.AddFile('file'+IntToStr(i), filenames[i]);
ResponseText := IdHTTP1.Post(url, FormData);
Memo1.Text := ResponseText; //debug
finally
FormData.Free;
end;
end;
procedure TForm1.btnSendbyElastic(Sender: TObject);
var
Params, Filenames : TStringList;
url, Afilename : string;
begin
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
Params := TStringList.Create;
try
Params.Add('apikey=' + ELASTIC_MAIL_API_KEY);
Params.Add('from=' + ELASTIC_EMAIL_FROM_EMAIL);
Params.Add('fromname=' + ELASTIC_EMAIL_FROM_NAME);
Params.Add('Subject=' + 'The Subject');
Params.Add('bodyHtml=' + '<h1>Html Body</h1>');
Params.Add('bodyText=' + 'Text Body');
Params.Add('to=' + THE_RECIPIENT_ADDRESS);
Filenames := TStringList.Create;
try
Filenames.Add(Afilename);
url := ELASTIC_EMAIL_EMAIL_SEND;
Upload(url, params, filenames);
finally
Filenames.Free;
end;
finally
Params.Free;
end;
end;
最后,Elastic 的文档没有说明包含 non-ASCII/reserved 个字符的文件名所需的编码。关于在通过 HTTP 传输时应如何对此类文件名进行编码,存在相互冲突的标准。默认情况下,TIdMultipartFormDataStream
根据 RFC 2047 对文件名进行编码。如果这对 Elastic 来说是个问题(你的示例文件名中有 space 个字符,我忘记了 TIdMultipartFormDataStream
RFC 是否由于 spaces 对文件名进行了编码,希望不会),您可以通过将受影响文件的 TIdFormDataField.HeaderEncoding
属性 设置为 '8'
(对于 8 位)来禁用 TIdMultipartFormDataStream
的默认编码,然后您可以设置 TIdFormDataField.FileName
属性 到你想要的任何编码:
with FormData.AddFile('file'+IntToStr(i), filenames[i]) do
begin
HeaderEncoding := '8';
FileName := EncodeFilenameMyWay(ExtractFileName(filenames[i]));
end;
使用 Indy 的 idHTTP.post 发送一个或多个任何类型的文件以及其他参数时需要使用的正确代码是什么? (使用 Delphi 2009 和 Indy 10)
所讨论的 post 调用商业公司 API (ElasticEmail) 中的一个函数,该函数向参数之一中保存的收件人发送电子邮件。 (关于我正在调用的函数的文档的 link 是 here. 我有来自公司 here 的 C# 和其他语言的示例代码,我试图在下面的 Delphi 代码中复制该代码。
如果在过程 btnSendbyElastic 中,我注释掉行 Filenames.add(Afilename);
以便函数 Upload 不尝试附加文件,那么正确的调用似乎是在电子邮件成功发送时进行的API。
但是,如果我保留该行,以便函数 UpLoad
MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.Addfile('file'+inttostr(i), filenames[i],MIMEStr);
do 被执行,然后没有邮件被发送,服务器的响应是
{"success":false,"error":"One of files has invalid characters in file name."}
(文件 Afilename 确实存在于该位置,我已尝试使用单反斜杠和双反斜杠)
阅读关于此主题的其他 SO posts 我还尝试用以下循环替换 Function UpLoad 中的文件处理循环
for i := 0 to filenames.Count - 1 do
begin
MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.AddFile('file'+inttostr(i), filenames[i],MIMEStr);
AttachmentContent := TFileStream.Create(filenames[i],fmOpenRead);
try
FormData.AddFormField(AttachmentContent.ToString,filenames[i]);
finally
AttachmentContent.free;
end;
end;
这一次,即使使用 Filenames.add(Afilename);
中指定的文件名,电子邮件也能正确发送,但收件人看不到附件。
在许多其他问题中,我已经阅读了这些可能重复的 SO 问题
Nodejs POST request multipart/form-data
特别是
(这几乎正是我想要做的)但我仍然看不出我在代码中做错了什么以及我需要做些什么来纠正它。
这是我正在使用的代码(UPPER_CASE 标识符是在别处定义的常量)
PS 我在英国,很抱歉回复美国的时间延迟 comments/answers
function TForm1.Upload(url: string; params, filenames: Tstringlist): string;
var
FormData : TIdMultiPartFormDataStream;
MIMEStr, ResponseText : string;
i : integer;
begin
try
FormData := TIdMultiPartFormDataStream.Create;
for i := 0 to params.Count - 1 do
FormData.AddFormField(params.Names[i],params.values[params.Names[i]]);
for i := 0 to filenames.Count - 1 do
begin
MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.Addfile(filenames[i], filenames[i],MIMEStr);
end;
ResponseText :=IdHTTP1.Post(url, FormData);
Memo1.Text := ResponseText; //debug
finally
FormData.free;
end;
end;
procedure TForm1.btnSendbyElastic(Sender: TObject);
var
Params, Filenames : Tstringlist;
url, Afilename : string;
begin
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
Params := Tstringlist.Create;
Filenames := Tstringlist.Create;
try
Params.add('apikey=' + ELASTIC_MAIL_API_KEY) ;
Params.add('from=' + ELASTIC_EMAIL_FROM_EMAIL) ;
Params.add('fromname=' + ELASTIC_EMAIL_FROM_NAME) ;
Params.add('Subject=' + 'The Subject') ;
Params.add('bodyHtml=' + '<h1>Html Body</h1>') ;
Params.add('bodyText=' + 'Text Body') ;
Params.add('to=' + THE_RECIPIENT_ADDRESS) ;
Filenames.add(Afilename); //*** comment out this line and an email is sent correctly
url := ELASTIC_EMAIL_EMAIL_SEND ;
Upload (url , params, filenames );
finally
Params.free;
Filenames.free;
end;
函数GetMIMETypeFromFile
定义在Indy单元idGlobalProtocols中。我没有写它,我只是调用它。不过我已经按照要求转载在这里了
function GetMIMETypeFromFile(const AFile: TIdFileName): string;
var
MIMEMap: TIdMIMETable;
begin
MIMEMap := TIdMimeTable.Create(True);
try
Result := MIMEMap.GetFileMIMEType(AFile);
finally
MIMEMap.Free;
end;
end;
我发现您的代码存在一些问题。
您在文件路径中错误地转义了 \
个字符。这在 C 和 C++ 等语言中是必需的,但在 Delphi 中根本不需要,所以去掉它。
改变这个:
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
为此:
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
我看到的下一个问题是您在将文件附件字段添加到 TIdMultipartFormDataStream
时没有正确命名它们。
调用 AddFile()
时,您将按原样将完整的文件路径传递给 AFieldName
参数,而不是使用 file0
、file1
等名称如 Elastic 的示例所示。
改变这个:
FormData.Addfile(filenames[i], filenames[i],MIMEStr);
对此1:
FormData.AddFile('file'+IntToStr(i), filenames[i], MIMEStr);
1:仅供参考,无需手动调用 GetMIMETypeForFile()
,如果您不提供字符串,AddFile()
会在内部为您调用 GetMIMETypeForFile()
AContentType
参数,例如FormData.AddFile('file'+IntToStr(i), filenames[i]);
当您尝试使用 AddFormField()
而不是 AddFile()
添加附件时,您犯了类似的错误。您将每个文件的实际数据内容用于 AFieldName
参数,而不是将内容用于 AFieldValue
参数。
在这种情况下,更改为:
FormData.AddFormField(AttachmentContent.ToString,filenames[i]);
为此:
FormData.AddFormField('file'+IntToStr(i), AttachmentContent.ToString, '', MIMEStr, filenames[i]);
或者,由于您自己打开 TFileStream
对象,您可以使用重载的 AddFormField()
方法,该方法将 TStream
作为输入(只是确保不要释放 TStream
个对象,直到您使用完 TIdMultipartFormDataStream
!):
AttachmentContent := TFileStream.Create(filenames[i], fmOpenRead);
FormData.AddFormField('file'+IntToStr(i), MIMEStr, '', AttachmentContent, filenames[i]);
话虽如此,试试这样的东西:
function TForm1.Upload(url: string; params, filenames: TStrings): string;
var
FormData : TIdMultiPartFormDataStream;
ResponseText : string;
i : integer;
begin
FormData := TIdMultiPartFormDataStream.Create;
try
for i := 0 to params.Count - 1 do
FormData.AddFormField(params.Names[i], params.ValueFromIndex[i]);
for i := 0 to filenames.Count - 1 do
FormData.AddFile('file'+IntToStr(i), filenames[i]);
ResponseText := IdHTTP1.Post(url, FormData);
Memo1.Text := ResponseText; //debug
finally
FormData.Free;
end;
end;
procedure TForm1.btnSendbyElastic(Sender: TObject);
var
Params, Filenames : TStringList;
url, Afilename : string;
begin
Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';
Params := TStringList.Create;
try
Params.Add('apikey=' + ELASTIC_MAIL_API_KEY);
Params.Add('from=' + ELASTIC_EMAIL_FROM_EMAIL);
Params.Add('fromname=' + ELASTIC_EMAIL_FROM_NAME);
Params.Add('Subject=' + 'The Subject');
Params.Add('bodyHtml=' + '<h1>Html Body</h1>');
Params.Add('bodyText=' + 'Text Body');
Params.Add('to=' + THE_RECIPIENT_ADDRESS);
Filenames := TStringList.Create;
try
Filenames.Add(Afilename);
url := ELASTIC_EMAIL_EMAIL_SEND;
Upload(url, params, filenames);
finally
Filenames.Free;
end;
finally
Params.Free;
end;
end;
最后,Elastic 的文档没有说明包含 non-ASCII/reserved 个字符的文件名所需的编码。关于在通过 HTTP 传输时应如何对此类文件名进行编码,存在相互冲突的标准。默认情况下,TIdMultipartFormDataStream
根据 RFC 2047 对文件名进行编码。如果这对 Elastic 来说是个问题(你的示例文件名中有 space 个字符,我忘记了 TIdMultipartFormDataStream
RFC 是否由于 spaces 对文件名进行了编码,希望不会),您可以通过将受影响文件的 TIdFormDataField.HeaderEncoding
属性 设置为 '8'
(对于 8 位)来禁用 TIdMultipartFormDataStream
的默认编码,然后您可以设置 TIdFormDataField.FileName
属性 到你想要的任何编码:
with FormData.AddFile('file'+IntToStr(i), filenames[i]) do
begin
HeaderEncoding := '8';
FileName := EncodeFilenameMyWay(ExtractFileName(filenames[i]));
end;