file - 如何在 TIdMultiPartFormDataStream 中包含文件以与 Indy IdHTTP1.Post 一起使用?
问题描述
我需要使用 Indy 的 idHTTP.post 发送一个或多个任何类型的文件以及其他参数的正确代码是什么?(使用 Delphi 2009 和 Indy 10)
有问题的帖子调用了商业公司 API (ElasticEmail) 中的一个函数,该函数向其中一个参数中保存的收件人发送电子邮件。(关于我正在调用的函数的文档的链接在这里。我在这里 有来自公司的 C# 和其他语言的示例代码, 我试图在下面的 Delphi 代码中复制该代码。
如果在过程 btnSendbyElastic 中注释掉该行Filenames.add(Afilename);
,以便函数 Upload 不尝试附加文件,则似乎在 API 成功发送电子邮件时进行了正确的调用。但是,如果我保留该行,以便函数 UpLoad 中的行
MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.Addfile('file'+inttostr(i), filenames[i],MIMEStr);
执行,然后不发送电子邮件,服务器的响应是
{"success":false,"error":"其中一个文件的文件名中包含无效字符。"}
(文件 Afilename 确实存在于该位置,我尝试过使用单反斜杠和双反斜杠)
阅读有关此主题的其他 SO 帖子,我还尝试将 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 问题
使用 indy / delphi 组件通过 https 发布文件
特别是
使用 Indy TidHttp 组件通过 sendgrid 发送邮件文件附件
(这几乎正是我想要做的)但我仍然看不到我在代码中做错了什么以及我需要做些什么来纠正它。
这是我正在使用的代码(UPPER_CASE 标识符是在别处定义的常量)
PS我在英国,所以很抱歉延迟回复美国的评论/答案
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
参数,而不是像 Elastic 示例中所示的那样使用file0
、file1
等名称。
改变这个:
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()
将 a 作为输入的重载方法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 的文档没有说明文件名中包含非 ASCII/保留字符所需的编码。对于通过 HTTP 传输时如何编码此类文件名,存在相互冲突的标准。默认情况下,TIdMultipartFormDataStream
根据RFC 2047对文件名进行编码。如果这对 Elastic 来说是个问题(您的示例文件名中有空格字符,我忘记了TIdMultipartFormDataStream
RFC 是否因为空格对文件名进行编码,希望不会),您可以TIdMultipartFormDataStream
通过设置受影响文件的TIdFormDataField.HeaderEncoding
属性为'8'
(对于 8 位),然后您可以将该TIdFormDataField.FileName
属性设置为您想要的任何编码:
with FormData.AddFile('file'+IntToStr(i), filenames[i]) do
begin
HeaderEncoding := '8';
FileName := EncodeFilenameMyWay(ExtractFileName(filenames[i]));
end;
推荐阅读
- android - 查看 APK 时的警告消息
- javascript - 带有证书的 NodeJS REST 调用。我怎么知道要使用哪个文件?
- javascript - 当点击没有在内容之外启动时,防止模式关闭?
- html - 将最大宽度设置为详细信息标签而不影响下拉箭头
- javascript - 我有 40 个来自 API 的数组。我希望所有 40 个数组的值都在一个数组中。我如何实现这一目标?
- statistics - 当我们有标准偏差时如何计算总体均值的置信区间
- sql - 如何将 json 数据从 sql 表列检索为单独的表
- regex - 如何编写自定义正则表达式来仅验证十进制浮点数 0.001 到 0.800?
- swift - 将一个 SwiftUI 视图从另一个视图下方滑出
- angular - 如何为对象添加功能?