TIdHTTP Post 到 API 并将响应保存为 .pdf 文件

TIdHTTP Post to API and save response as a .pdf file

根据提供的文档,我正在用一些 parameters 调用 API URL

API 的设置方式,响应应该是 .pdf 文件的自动下载。

参数为:

文档表明它应该作为 Post 请求提出。

我有以下代码:

var
FileName : string;
URL: String;
Params: TStringList;
memStream: TMemoryStream;
...

begin
FileName:= MainModule.PDFfileName + '.pdf';

 begin
 URL := 'https://someURL/view_integrated_pdf.php?';
 Params := TStringList.Create;
 memStream := TMemoryStream.Create;

  try
  Params.Add('number='+MainModule.number+'');
  Params.Add('username='+MainModule.User+'');
  Params.Add('client_id='+MainModule.clientID+'');
  Params.Add('user_pass='+MainModule.Pass+'');
  Params.Add('language=en');

  MainModule.IdHTTP.Post(URL, Params, memStream);

  finally
  memStream.SaveToFile(ServerModule.FilesFolderPath+'\pdfs\'+FileName);
  Params.Free;
  memStream.Free;

  end;
 end;

pdfForm.ShowModal();

end;

如果我在浏览器中尝试生成 URLparameters - 它会自动下载 pdf 文件 API 给我的名称 numberParameter.pdf

如果我使用提供的代码在 Delphi 中执行此操作,则 10 次中有 8 次会保存 pdf file1 KB Size(通常在 32 之间和 100 对于成功的文件),当我尝试使用我的 pdfForm 和随后的 viewer 在程序中打开它时,查看器抛出错误 "Invalid PDF structure"

更新

根据评论,在 Notepad++ 中打开生成的 1kb PDF 显示的内容只是 Error username.

这很令人费解,因为我检查过并且传递的用户名是准确的 + 如果我粘贴完全相同的 URL 和提交的参数值,在浏览器中,它可以完美运行并为我提供正确的 PDF。

If I try the resulting URL and parameters in the browser - it auto-downloads the pdf file the API gives me with the name numberParameter.pdf

在浏览器中做到这一点的唯一方法是将参数放在 URL 的查询组件中,例如:

https://someURL/view_integrated_pdf.php?number=...&username=...&client_id=...&user_pass=...&language=en

... 而不是在 POST 主体中,就像您的代码所做的那样。此外,浏览器会发送 GET 请求,而不是 POST 请求。除非您填写 HTML 网络表单(即 <form action="<URL>" method="POST" ...>)并提交。这是 API 文档所说的吗?

由于您没有从文档中提供有关此服务器实际期望的任何详细信息,因此我们无法真正告诉您您的代码是正确还是错误。但是您的手动测试似乎与您声称文档中所说的服务器期望的内容相矛盾。

If I do it in Delphi using the provided code, 8 out of 10 times it saves a pdf file with a 1 KB Size (normally it is between 32 and 100 for the successful file) and when I try to open it in the program using my pdfForm and subsequent viewer, the viewer throws an error "Invalid PDF structure"

根据评论,您说您的文件收到文本错误消息。 TIdHTTP 会将此类文本保存到您的 TMemoryStream 仅当 为:

  • HTTP 服务器没有 报告 HTTP 级别的错误,即它正在使用 HTTP 2xx 成功响应发送文本消息.我怀疑这就是你的情况。默认情况下,如果服务器使用 proper HTTP 错误代码,TIdHTTP 将引发包含文本消息的 EIdHTTPProtocolExceptionNOT 将文本保存到您的 TMemoryStream 中。

  • HTTP 服务器 IS 在 HTTP 级别报告错误,但您同时使用了 hoNoProtocolErrorExceptionhoWantProtocolErrorContent 标志在 TIdHTTP.HTTPOptions 属性 中。在这种情况下,TIdHTTP 不会引发 EIdHTTPProtocolException 而是会保存服务器按原样发送到您的 TMemoryStream.

    的任何数据

由于显然没有引发 HTTP 异常,您必须先验证服务器的响应,然后才能在 pdfForm 中使用下载的数据,即查看 TIdHTTP.Response.ContentType and/or TIdHTTP.Response.ContentDisposition 属性 了解服务器是否实际发送了 PDF 文件。

This is puzzling since I checked and the username being passed is accurate + if I paste the exact same URL with parameter values filed, in the browser, it works perfectly and gives me the correct PDF.

嗯,一方面,您的代码中有错字:numberr 字段需要改为 number

除此之外,将 URL 放入浏览器与您的代码执行的操作不同,因此请尝试更改您的代码以模仿您手动执行的操作,例如:

uses
  ..., IdGlobalProtocols, IdHTTP, IdURI;

...

var
  URL : string;
  memStream: TMemoryStream;
begin
  // All parameters into the URI for a HTTP GET request
  URL := 'https://someURL/view_integrated_pdf.php'
       + '?number=' + TIdURI.ParamsEncode(MainModule.number)
       + '&username=' + TIdURI.ParamsEncode(MainModule.User)
       + '&client_id=' + TIdURI.ParamsEncode(MainModule.clientID)
       + '&user_pass=' + TIdURI.ParamsEncode(MainModule.Pass)
       + '&language=en';

  memStream := TMemoryStream.Create;
  try
    MainModule.IdHTTP.Get(URL, memStream);

    // Is it really PDF? Other formats such as plaintext is not wanted.
    if not IsHeaderMediaType(MainModule.IdHTTP.ContentType, 'application/pdf') then Exit;

    memStream.SaveToFile(ServerModule.FilesFolderPath + '\pdfs\' + MainModule.PDFfileName + '.pdf');
  finally
    memStream.Free;
  end;

  pdfForm.ShowModal;
end;

如果这不起作用,请更新您的问题以提供实际文档。