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;

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

源代码下载

相关推荐
海阔天空任鸟飞~6 小时前
Linux 权限 777
linux·运维·服务器
IT瑞先生8 小时前
docker-compose下快速部署实操——持续更新...
运维·docker·容器
无锡银洲自动化8 小时前
工业模拟量采集痛点分析:IDCB-4E/DR/Y测量前端解决方案
运维·产品运营
Kina_C9 小时前
Linux iptables 防火墙原理与实操——从四表五链到 NAT 配置
linux·运维·服务器·iptables
A洛9 小时前
Codex 实战:一句话完成 Temu 商品图采集与印花抠图自动化
运维·人工智能·自动化·codex
Urbano10 小时前
服装工厂卫衣量产提质增效方案:工序自动化替代、产能优化、多品牌设备选型及落地案例
运维·自动化
GlobalSign数字证书10 小时前
自动化时代,如何高效部署SSL证书?
运维·自动化·ssl
大侠锅锅13 小时前
第 19 篇:远程运维体系——指标、SSH、日志与 OTA 升级
运维·ssh
灵机一物14 小时前
企业选型参考:2026 CXL 内存扩展方案六强测评与落地指南
服务器·网络·数据库·人工智能
Web极客码14 小时前
如何用三段式确定性剪枝,为 LLM Agent 砍掉 35% 的 Token 成本?
服务器·人工智能·算法·机器学习