代码之家  ›  专栏  ›  技术社区  ›  Luiz Alves

用CGI运行Delphi应用程序并将结果发送到前端

  •  0
  • Luiz Alves  · 技术社区  · 5 年前

    我有一个winCGI可执行脚本返回一个图像。我使用Intraweb作为服务器。

    cgi来自第三方,我无法更改您的代码。 当我查询图像时,我有下一个从CGI返回的代码:

    'Content-type: image/gif'#$A'Access-Control-Allow-Origin: *'#$A#$A'GIF89a@'#1'@'#1#0#0#0'!ÿ'#$B'NETSCAPE2.0'#3#1'ÿÿ'#0'!ù'#4#0'!'#0#0#0','#0#0#0#0'@'#1'@'#1'‡'#0#0#0#1#1#1#2#2#2#3#3#3#4#4#4#5#5#5#6#6#6#7#7#7#8#8#8#9#9#9#$A#$A#$A#$B#$B#$B#$C#$C#$C#$D#$D#$D#$E#$E#$E#$F#$F#$F#$10#$10#$10#$11#$11#$11#$12#$12#$12#$13#$13#$13#$14#$14#$14#$15#$15#$15#$16#$16#$16#$17#$17#$17#$18#$18#$18#$19#........
    

    我需要将图像发送到浏览器中的前端应用程序。

    <div>
     <img src="getImage(1)">
    </div>
    

    如何将服务器上图像的内容文本修复为前端的有效图像?

    function RunCGIOutput(CommandLine: string; Stream:TStream;Folder: string = ''): string;
    const
      CReadBuffer = 2400;
    var
      saSecurity: TSecurityAttributes;
      hRead: THandle;
      hWrite: THandle;
      suiStartup: TStartupInfo;
      piProcess: TProcessInformation;
      dRead: DWORD;
      Handle,WasOK:Boolean;
      Buffer: array[0..CReadBuffer] of AnsiChar;
      BytesRead: Cardinal;
      WorkingDirP,EnvBlock:PChar;
    
    begin
      saSecurity.nLength := SizeOf(TSecurityAttributes);
      saSecurity.bInheritHandle := true;
      saSecurity.lpSecurityDescriptor := nil;
      EnvBlock := BuildEnvBlock(True);
      if Folder <> '' then WorkingDirP := PChar(Folder)
      else WorkingDirP := nil;
      if CreatePipe(hRead, hWrite, @saSecurity, 0) then
        try
          FillChar(suiStartup, SizeOf(TStartupInfo), #0);
          suiStartup.cb := SizeOf(TStartupInfo);
          suiStartup.hStdInput := hRead;
          suiStartup.hStdOutput := hWrite;
          suiStartup.hStdError := hWrite;
          suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
          suiStartup.wShowWindow := SW_HIDE;
          Handle:=CreateProcess(nil, PChar(CommandLine), @saSecurity, @saSecurity, true, CREATE_UNICODE_ENVIRONMENT, EnvBlock, WorkingDirP, suiStartup,
            piProcess);
    
        CloseHandle(hWrite);
        if Handle then
          try
            repeat
                  WasOK := ReadFile(hRead, Buffer, CReadBuffer, BytesRead, nil) and (BytesRead>0);
                  if WasOK then
                    Stream.WriteBuffer(Buffer, BytesRead);
            until not WasOK;
            WaitForSingleObject(piProcess.hProcess, INFINITE);
          finally
            CloseHandle(piProcess.hThread);
            CloseHandle(piProcess.hProcess);
          end;
      finally
        CloseHandle(hRead);
        StrDispose(EnvBlock);
      end;
    end;
    
    
    //Run CGI, take image and send to browser
    function TContentImaqge.Execute(aRequest: THttpRequest; aReply: THttpReply;
      const aPathname: string; aSession: TIWApplication;
      aParams: TStrings): boolean;
    var
      s,wresult,saida,LocalDoc:string;
      i:integer;
      Stream:TMemoryStream;
    begin
        Result:=True;
        LocalDoc:=TIWAppInfo.GetAppPath + 'wwwroot\cgi-bin\newweb\dgate.exe';
        Stream:=TMemoryStream.Create;
        saida:=RunCGIOutput(LocalDoc,Stream,TIWAppInfo.GetAppPath + 'wwwroot\cgi-bin\newweb\');
        Stream.Position:=0;
        saida:=ReadStringFromStream(Stream, -1, IndyTextEncoding_OSDefault);
        with aReply do
         begin
           ResetReplyType;
           Code := 200;
           ContentType := MIME_GIF; // MIME_HTML;
           SendStream(Stream);
        end;
    end;
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Peter Wolf    5 年前

    你的 CGI 模块生成原始 HTTP response 它应该按原样由IW内容处理程序传输。响应消息包括:

    1. HTTP标题行
    2. 分隔页眉和正文的空行
    3. 正文(案例中的实际图像数据)

    THTTPReply 似乎无法控制原始的HTTP响应。它提供了专门的接口来分别处理状态、标题和正文。这就是为什么您需要预处理从CGI返回的流,并通过空行将头与正文分开。然后你可以通过 蒂普雷 . 也许你也只想白名单一些标题,而忽略其余的。

    在您使用的代码片段中 ReadStringFromStream 这是毫无意义的,因为无论如何都要丢弃返回的值。它移动的位置 Stream aReply.SendStream(Stream) 从一开始就发送整个流内容。

    另一点是你用 IndyTextEncoding_OSDefault 作为最后一个参数 ,这可能是错误的,因为 HTTP header is encoded in ASCII IndyTextEncoding_ASCII .

    请改为使用以下代码:

    function TContentImaqge.Execute(aRequest: THttpRequest; aReply: THttpReply;
      const aPathname: string; aSession: TIWApplication;
      aParams: TStrings): Boolean;
    var
      CGIOutput, ContentStream: TMemoryStream;
      LocalDoc, Line: string;
      CharPos: Integer;
    begin
      Result := True;
      CGIOutput := TMemoryStream.Create;
      try
        LocalDoc := TIWAppInfo.GetAppPath + 'wwwroot\cgi-bin\newweb\dgate.exe';
        RunCGIOutput(LocalDoc, CGIOutput, TIWAppInfo.GetAppPath + 'wwwroot\cgi-bin\newweb\');
    
        if ReadLnFromStream(CGIOutput, Line, -1, IndyTextEncoding_ASCII) then
        begin
          { process status line }
          CharPos := Pos(' ', Line);
          if CharPos > 0 then
          begin
            aReply.Code := StrToInt(Copy(Line, CharPos + 1, 3));
            CharPos := Pos(' ', Line, CharPos);
            if CharPos > 0 then
              aReply.CodeText := Copy(Line, CharPos + 1);
          end;
    
          { process headers (copy headers as they are) }
          while ReadLnFromStream(CGIOutput, Line, -1, IndyTextEncoding_ASCII) and (Line <> '') do
            aReply.Headers.Add(Line);
    
          { at this point CGIOutput.Position is at the beginning of body, so let's just copy
            the content to a separate memory stream }
          ContentStream := TMemoryStream.Create;
          try
            ContentStream.CopyFrom(CGIOutput, CGIOutput.Size - CGIOutput.Position);
          except
            ContentStream.Free;
            raise;
          end;
          aReply.SendStream(ContentStream);
        end
        else
        begin
          aReply.Code := 500;
          aReply.CodeText := RSHTTPInternalServerError;
          aReply.WriteString('CGI module returned malformed response.');
        end;
      finally
        CGIOutput.Free;
      end;
    end;
    

    另外,请注意,您所附的CGI输出不包含状态行,直接以标题开头(内容类型:…)。如果这是从CGI得到的,那么您应该更新代码来处理这样的情况。