Delphi - IndyHttpServer接收上传文件

我使用delphi12CE+Indy中的idhttpserver接收post上来的图片,代码如下:

Delphi 复制代码
// 处理 POST 请求 
if SameText(ARequestInfo.Command, 'POST') and SameText(ARequestInfo.Document, '/upload') then 
begin 
    if ARequestInfo.PostStream <> nil then 
        begin 
            outPath := TPath.Combine(FUploadDir, FormatDateTime('yyyymmdd_hhnnss_zzz',    Now).Replace('.', '') + '.jpg'); 
            FileStream := TFileStream.Create(outPath, fmCreate); 
            ARequestInfo.PostStream.Position := 0; 
            FileStream.CopyFrom(ARequestInfo.PostStream, ARequestInfo.PostStream.Size); { Copy 流 } FileStream.Free; 
        end; 
end;

可以接收,但是接收的图片文件都无法打开,使用NotePad++打开发现图片文件是这样的:

------WebKitFormBoundarybBNrZRN79bc6VhED Content-Disposition: form-data; name="name" all ------WebKitFormBoundarybBNrZRN79bc6VhED Content-Disposition: form-data; name="file"; filename="th_d6bee6de9e633c8618fab537b2ae3b3chd_042427.jpg" Content-Type: image/jpeg ??JFIF ?C

这是文件头,文件尾部还有:

------WebKitFormBoundarybBNrZRN79bc6VhED--

似乎是除了图片文件数据外头部和尾部还有其他的东西,如何解决这个问题呢?

经过分析,现在的 POST 请求是 multipart/form-data ,而上面代码直接把 ARequestInfo.PostStream 整个保存成文件了,导致文件里面不仅有图片二进制,还包含了表单的边界(boundary)、Content-Disposition、Content-Type 等文本信息。

所以图片打不开是因为数据污染了。

Indy 的 TIdHTTPServer 在处理 multipart/form-data 时,并不会自动帮你分离文件,需要你手动解析表单。

解决方法是直接用 Indy 的 TIdMultiPartFormDataStream + ARequestInfo.RawHeaders 手动提取,下面是一个 Delphi12 CE 可运行的示例代码:

Delphi 复制代码
function ExtractFileFromRequest(ARequestInfo: TIdHTTPRequestInfo;
  out AFileName: string): TMemoryStream;
var
  LBoundary, LBoundaryMarker: string;
  LContent: TBytes;
  LContentStr, HeaderStr: string;
  PartStart, PartEnd, HeaderEnd: Integer;
begin
  Result := nil;
  AFileName := '';

  if not Assigned(ARequestInfo.PostStream) then
    Exit;

  // boundary
  LBoundary := ARequestInfo.RawHeaders.Values['Content-Type'];
  if LBoundary = '' then Exit;
  LBoundary := Copy(LBoundary, Pos('boundary=', LBoundary) + 9, MaxInt);
  if LBoundary = '' then Exit;

  LBoundaryMarker := '--' + LBoundary;

  // 读取全部内容
  SetLength(LContent, ARequestInfo.PostStream.Size);
  ARequestInfo.PostStream.Position := 0;
  ARequestInfo.PostStream.ReadBuffer(LContent[0], Length(LContent));
  LContentStr := TEncoding.ANSI.GetString(LContent);

  // 遍历所有 part
  PartStart := Pos(LBoundaryMarker, LContentStr);
  while PartStart > 0 do
  begin
    // 找 header 结束
    HeaderEnd := Pos(#13#10#13#10, LContentStr, PartStart);
    if HeaderEnd = 0 then Break;

    HeaderStr := Copy(LContentStr, PartStart, HeaderEnd - PartStart);

    if ContainsText(HeaderStr, 'filename="') then
    begin
      // 文件名
      AFileName := Copy(HeaderStr, Pos('filename="', HeaderStr) + 10, MaxInt);
      AFileName := Copy(AFileName, 1, Pos('"', AFileName) - 1);

      // 文件起点
      Inc(HeaderEnd, 4);

      // 找下一个 boundary
      PartEnd := Pos(LBoundaryMarker, LContentStr, HeaderEnd);
      if PartEnd = 0 then Break;

      // ⚠️ 精确剔除 boundary 前的换行
      while (PartEnd > HeaderEnd) and
        ((LContent[PartEnd - 2] = 13) or (LContent[PartEnd - 2] = 10)) do
        Dec(PartEnd);

      // 复制二进制
      Result := TMemoryStream.Create;
      Result.WriteBuffer(LContent[HeaderEnd - 1], PartEnd - HeaderEnd + 1);
      Result.Position := 0;
      Exit;
    end;

    // 下一个 part
    PartStart := Pos(LBoundaryMarker, LContentStr, HeaderEnd);
  end;
end;

我把经过测试可以正常运行的代码封装到了一个单元中,下面是下载地址:

源代码下载

相关推荐
云飞云共享云桌面4 分钟前
8-10位研发3D(sw、ug、creo)画图如何共享一台工作站?
运维·服务器·网络·数据库·3d·电脑
Turboex邮件分享5 分钟前
邮件系统中的抗拒绝服务(DDoS)攻击防护
运维·网络·安全·ddos·软件需求
成为你的宁宁8 分钟前
【docker镜像加速器配置】
运维·docker·容器
三道渊20 分钟前
Linux进程通信与信号处理全解析
linux·服务器·网络
AI_Claude_code22 分钟前
ZLibrary访问困境方案六:自建RSS/Calibre内容同步服务器的完整指南
运维·服务器·网络·爬虫·python·tcp/ip·http
Java后端的Ai之路23 分钟前
sudo 命令详解:Linux 权限管理的“万能钥匙“
linux·运维·服务器·sudo
AI_零食24 分钟前
开源鸿蒙跨平台Flutter开发:生日纪念日提醒应用
运维·flutter·开源·harmonyos·鸿蒙
ID_1800790547324 分钟前
Python解析小红书(XHS)笔记评论 API,json数据返回参考
java·服务器·数据库
mhkxbq32 分钟前
昆仑G5580、G5680 V2、G2280及泰山鲲鹏200,AI大数据优选服务器
大数据·服务器·人工智能
艾莉丝努力练剑41 分钟前
【QT】Qt常用控件与布局管理深度解析:从原理到实践的架构思考
linux·运维·服务器·开发语言·网络·qt·架构