如何在TIdMultiPartFormDataStream中包含文件以与Indy IdHTTP1.Post一起使用? [英] How do I include a file in a TIdMultiPartFormDataStream for use with Indy IdHTTP1.Post?

查看:276
本文介绍了如何在TIdMultiPartFormDataStream中包含文件以与Indy IdHTTP1.Post一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Indy的idHTTP.post来发送一个或多个任何类型的文件以及其他参数时,需要使用什么正确的代码? (使用Delphi 2009和Indy 10)

What is the correct code that I need to use to send one or more files of any type along with other parameters using Indy's idHTTP.post? (using Delphi 2009 and Indy 10)

所讨论的帖子调用了商业公司的API(ElasticEmail)中的一个函数,该函数将电子邮件发送到参数之一中包含的收件人. (指向我要调用的函数的文档的链接位于此处. 我有来自公司

The post in question calls a function in a commercial company's API (ElasticEmail) that sends out emails to the recipients held in one of the parameters. (A link to the documentation on the function I am calling is here. I have example code in C# and other languages from the company here and I have tried to replicate that code in my Delphi code below.

如果在btnSendbyElastic程序中,我注释掉了Filenames.add(Afilename);行,以便函数Upload不尝试附加文件,那么当API成功发送电子邮件时,似乎进行了正确的调用. 但是,如果我保留该行,以便函数UpLoad

If, in Procedure btnSendbyElastic, I comment out the line Filenames.add(Afilename); so that the function Upload makes no attempt to attach a file,then the correct call seems to be made as the email gets sent successfully by the API. However, if I leave that line in so that the lines in function UpLoad

MimeStr := GetMIMETypeFromFile(filenames[i]);
FormData.Addfile('file'+inttostr(i), filenames[i],MIMEStr); 

执行,然后将不发送电子邮件并且服务器的响应为

do get executed, then no email is sent and the response from the server is

{成功":false,错误":其中一个文件的文件名中包含无效字符."}

{"success":false,"error":"One of files has invalid characters in file name."}

(文件Afilename确实存在于该位置,并且我尝试使用单反斜杠和双反斜杠)

(The file Afilename does exist at that location and I have tried with single and double backslashes)

在阅读有关该主题的其他SO帖子时,我还尝试用以下循环替换Function UpLoad中的文件处理循环

Reading other SO posts on this topic I also tried replacing the file processing loop in Function UpLoad with the following loop instead

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);中指定的文件名,也可以正确发送电子邮件,但收件人看不到附件.

This time, even with a filename specified in Filenames.add(Afilename);, the email is sent correctly but the recipient sees no attachment.

在许多其他问题中,我已经阅读了这些可能重复的SO问题

Among many others, I have read these possible duplicate SO questions

带有indy的Http帖子

使用indy/delphi组件通过https发布文件

将文件作为表单的一部分发布

Nodejs POST请求multipart/form-data

特别是

使用Indy TidHttp组件通过sendgrid发送电子邮件文件附件

(这几乎完全是我想做的事情),但是我仍然看不到我的代码做错了什么以及纠正它需要做些什么.

(which is almost exactly what I am trying to do) but I still cannot see what I am doing wrong in my code and what I need to do to correct it.

这是我正在使用的代码(UPPER_CASE标识符是在其他地方定义的常量)

Here is the code I am using (UPPER_CASE identifiers are constants defined elsewhere)

我在英国,很抱歉在回复美国评论/答案时出现时间延迟

PS I'm in the UK so apologies for the time delay in responding to US 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在印地单位idGlobalProtocols中定义.我没有写,我只是称呼它.但是我已经按照要求在这里复制了它

The function GetMIMETypeFromFile is defined in the Indy unit idGlobalProtocols. I didn't write it, I just call it. But I have reproduced it here as requested

function GetMIMETypeFromFile(const AFile: TIdFileName): string;
var
  MIMEMap: TIdMIMETable;
begin
  MIMEMap := TIdMimeTable.Create(True);
  try
    Result := MIMEMap.GetFileMIMEType(AFile);
  finally
    MIMEMap.Free;
  end;
end;

推荐答案

我发现您的代码存在一些问题.

I see a few problems with your code.

您错误地在文件路径中转义了\个字符.在诸如C和C ++之类的语言中,这是必需的,但在Delphi中根本不需要,因此请消除它.

You are erroneously escaping \ characters in your file paths. That is necessary in languages like C and C++, but is not needed in Delphi at all, so get rid of it.

更改此:

Afilename := 'C:\\Users\\Admin\\Documents\\arrival and departure small.pdf';

对此:

Afilename := 'C:\Users\Admin\Documents\arrival and departure small.pdf';

我看到的下一个问题是,将文件附件字段添加到TIdMultipartFormDataStream时没有正确命名文件附件字段.

The next problem I see is you are not naming the file attachment fields correctly when adding them to the TIdMultipartFormDataStream.

在调用AddFile()时,您将按原样将完整的文件路径传递给AFieldName参数,而不是像Elastic示例中所示的那样使用file0file1等名称.

When calling AddFile(), you are passing the complete file path as-is to the AFieldName parameter, instead of using names like file0, file1, etc like shown in Elastic's examples.

更改此:

FormData.Addfile(filenames[i], filenames[i],MIMEStr);

对此 1 :

FormData.AddFile('file'+IntToStr(i), filenames[i], MIMEStr);

1:仅供参考,如果您没有为AContentType参数提供字符串,例如FormData.AddFile('file'+IntToStr(i), filenames[i]);

1: FYI, there is no need to call GetMIMETypeForFile() manually, AddFile() calls GetMIMETypeForFile() internally for you if you do not provide a string for the AContentType parameter, eg FormData.AddFile('file'+IntToStr(i), filenames[i]);

当您尝试使用AddFormField()而不是AddFile()添加附件时,您犯了类似的错误.您将每个文件的实际数据内容用于AFieldName参数,而不是将内容用于AFieldValue参数.

You made a similar mistake when you tried to use AddFormField() instead of AddFile() to add attachments. You used each file's actual data content for the AFieldName parameter, instead of using the content for the AFieldValue parameter.

在这种情况下,请更改此内容:

In that case, change this:

FormData.AddFormField(AttachmentContent.ToString,filenames[i]);

对此:

FormData.AddFormField('file'+IntToStr(i), AttachmentContent.ToString, '', MIMEStr, filenames[i]);

或者,由于您自己打开了TFileStream对象,因此可以使用以TStream作为输入的重载AddFormField()方法(只是请确保在完成使用后才释放TStream对象) TIdMultipartFormDataStream!):

Or, since you were opening TFileStream objects yourself, you could use the overloaded AddFormField() method that takes a TStream as input (just be sure NOT to free the TStream objects until after you are done using the TIdMultipartFormDataStream!):

AttachmentContent := TFileStream.Create(filenames[i], fmOpenRead);
FormData.AddFormField('file'+IntToStr(i), MIMEStr, '', AttachmentContent, filenames[i]);

话虽如此,请尝试以下类似操作:

With that said, try something more like this:

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属性设置为所需的任何编码:

Lastly, Elastic's documentation does not say anything about the encoding needed for filenames that contain non-ASCII/reserved characters in it. And there are conflicting standards as to how such filenames should be encoded when transmitted over HTTP. By default, TIdMultipartFormDataStream encodes filenames according to RFC 2047. If that turns out to be a problem for Elastic to handle (your example filename has space characters in it, I forget whether TIdMultipartFormDataStream RFC-encodes a filename due to spaces or not, hopefully not), you can disable TIdMultipartFormDataStream's default encoding by setting an affected file's TIdFormDataField.HeaderEncoding property to '8' (for 8-bit) and then you can set the TIdFormDataField.FileName property to whatever encoding you want:

with FormData.AddFile('file'+IntToStr(i), filenames[i]) do
begin
  HeaderEncoding := '8';
  FileName := EncodeFilenameMyWay(ExtractFileName(filenames[i]));
end;

这篇关于如何在TIdMultiPartFormDataStream中包含文件以与Indy IdHTTP1.Post一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆