当我上次显示Delphi TWebBrowser网页后,最好的检测方式是什么?

6
我希望使用Delphi TWebBrowser在表单中显示一个“新闻”页面。该新闻页面是一个简单的HTML页面,我们不时地从各种工具中上传到我们的网站,并可能输出。展示效果很好,但我想知道我的应用程序是否自上次显示以来发生了更改,因此理想情况下,我想要获取其修改日期/时间或其大小/校验和。精度并不重要,最好不要依赖可能因使用“简单”工具编辑HTML文件(如Notepad)而失败的属性。查看网络上有几个文档修改java调用,但我真的不知道从哪里开始。我已经查看了Delphi的Winapi.WinInet单位中的众多调用,我看到我可以使用HTTP获取文件进行检查,但这似乎就像用大锤砸碎核桃一样。我也看不到任何文件日期时间功能,这使我认为我错过了一些明显的东西。我正在使用Delphi XE5。请问我应该朝哪个方向寻找?谢谢任何指针。

你是指Java还是Javascript?你感兴趣的特定网站(你知道但我们不知道)是否有实时更新的机制? - David Heffernan
3
根据服务器/网站不同,进行 HTTP 的 head 请求可能会返回 Last-Modified / Content-Length 头信息。 - kobik
@David:对于这个网站的东西,我不是很熟悉,但我们保持简单——根据需要上传更改后的HTML页面,所以没有什么高级技巧。 - Brian Frost
好的,所以你控制这两个方面。你应该在问题中说明这一点。 - David Heffernan
@David:谢谢,我已经注意到了。我还指定了我希望容忍对文件进行的任何编辑,例如记事本。 - Brian Frost
2个回答

4
您可以使用Indy的 TIdHTTP 发送一个HEAD请求,并检查Last-Modified/ Content-Length标头。
例如:
procedure TForm1.Button1Click(Sender: TObject);
var
  Url: string;
  Http: TIdHTTP;
  LastModified: TDateTime;
  ContentLength: Integer;  
begin
  Url := 'http://yoursite.com/newspage.html';
  Http := TIdHTTP.Create(nil);
  try
    Http.Head(Url);
    LastModified := Http.Response.LastModified;
    ContentLength := Http.Response.ContentLength;
    ShowMessage(Format('Last-Modified: %s ; Content-Length: %d', [DateTimeToStr(LastModified), ContentLength]));
  finally
    Http.Free;
  end;
end;

TWebBrowser.DocumentComplete事件被触发时,发出HEAD请求并存储LastModifiedContentLength变量。然后,定期发出HEAD请求以测试是否有更改(例如通过TTimer)。
这些标头参数取决于Web服务器实现,并且可能不会返回服务器上的文件系统日期时间(例如动态页面)。你的服务器可能根本不会返回这些参数。
例如,在IIS上的静态HTML页面中,Last-Modified返回文件系统上的最后修改日期时间,这正是你想要的。
对于动态内容(例如php、asp、.NET等),如果你控制Web服务器,你可以在服务器端添加自定义的HTTP响应标头来指示文件系统的日期时间(例如X-Last-Modified),或者将响应Last-Modified标头设置为你需要的值,并在客户端检查该标头。
如果你需要检查/哈希整个HTTP内容,你需要发出一个GET方法:http.Get(URL)

谢谢。LastModified实际上会访问文件系统的日期时间吗?例如,如果有人使用任何工具修改了文件,那么它是否返回修改时间?(就像Delphi的FileAge在本地磁盘上一样)。 - Brian Frost
3
@Brian,这完全取决于您拥有的服务器。您的服务器可能不会返回您提到的标头字段,甚至不会响应 HEAD 请求。 - TLama
@TLama,所以我可以使用Kobik上面的建议来获取整个页面作为流并使用哈希值,对吗? - Brian Frost
1
@Brian,获取页面不符合你“杀鸡焉用牛刀”的要求。你是否能使用本帖所述取决于你的服务器(我们不知道)能做什么。如果它是你自己编写的服务器,那么你肯定可以扩展它以返回在HEAD请求中提到的标头字段作为响应。这是正确的方法。我相信例如Apache也可以进行配置(只是猜测)。 - TLama
@TLama:这只是一个FTP站点,我们可以将一堆文件拖放到那里上传 :-) - Brian Frost
4
对于在IIS上的静态HTML页面,“Last-Modified”头部返回的是文件系统的日期和时间。 - kobik

1

感谢kobik、David和TLama的建议和指针,我意识到我实际上确实需要一个大锤子,最终想出了这个解决方案(可能不是第一个,也不是最后一个!)。我必须读取文件内容,因为这似乎是检测更改的更好方式。下面的代码从TTimer不频繁地调用“CheckForWebNewsOnTimer”,并使用Indy读取新闻页面,对其内容进行MD5哈希处理,并将其与存储在注册表中的先前哈希进行比较。如果内容发生变化或经过120天,该页面会弹出。代码有些问题,例如页面上链接图像的更改可能不会触发更改,但嘿,这只是新闻,而文本几乎总是会更改。

function StreamToMD5HashHex( AStream : TStream ) : string;
// Creates an MD5 hash hex of this stream
var
  idmd5 : TIdHashMessageDigest5;
begin
  idmd5 := TIdHashMessageDigest5.Create;
  try
    result := idmd5.HashStreamAsHex( AStream );
  finally
    idmd5.Free;
  end;
end;



function HTTPToMD5HashHex( const AURL : string ) : string;
var
  HTTP : TidHTTP;
  ST : TMemoryStream;
begin
  HTTP := TidHTTP.Create( nil );
  try
    ST := TMemoryStream.Create;
    try
      HTTP.Get( AURL, ST );
      Result := StreamToMD5HashHex( ST );
    finally
      ST.Free;
    end;
  finally
    HTTP.Free;
  end;
end;




function ShouldShowNews( const ANewHash : string; AShowAfterDays : integer ) : boolean;
const
  Section = 'NewsPrompt';
  IDHash  = 'LastHash';
  IDLastDayNum = 'LastDayNum';
var
  sLastHash : string;
  iLastPromptDay : integer;
begin


  // Check hash
  sLastHash := ReadRegKeyUserStr( Section, IDHash, '' );
  Result := not SameText( sLastHash, ANewHash );
  if not Result then
    begin
    // Check elapsed days
    iLastPromptDay := ReadRegKeyUserInt( Section, IDLastDayNum, 0 );
    Result := Round( Now ) - iLastPromptDay > AShowAfterDays;
    end;

  if Result then
    begin
    // Save params for checking next time.
    WriteRegKeyUserStr( Section, IDHash, ANewHash );
    WriteRegKeyUserInt( Section, IDLastDayNum, Round(Now) );
    end;
end;





procedure CheckForWebNewsOnTimer;
var
  sHashHex, S : string;
begin
  try
    S := GetNewsURL; // < my news address
    sHashHex := HTTPToMD5HashHex( S );
    If ShouldShowNews( sHashHex, 120 {days default} ) then
      begin
      WebBrowserDlg( S );
      end;

  except
    // .. ignore or save as info
  end;
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接