自动升级程序,客户端如何找出服务器更新的那些文件!如何实现同步的? (100分)

  • 主题发起人 主题发起人 nutian
  • 开始时间 开始时间
N

nutian

Unregistered / Unconfirmed
GUEST, unregistred user!
在局域网一个自动升级的程序,如何实现:
1、在主机上更新了几个文件或文件夹,客户端启动时,自动判断出这些更新了的文件或文件夹,并拷贝过来(只拷那些更新的)。要求判断过程尽量的短,让用户感觉不到是最好的。
2、那些同步的软件,是怎样实现的?网络游戏的自动升级是怎么判断升级那些文件的?
3、断点续传的原理是怎样的?
 
http://new.playicq.com/dispdocnew.php?id=3287
用这个控件
 
以下是我参考别人的代码改写的,全部给你:
procedure TMainForm.SoftUpdateProc;

{
var

P: PChar;
S: Integer;
BS: TADOBlobStream;
begin
if not (ADOTable1.State in [dsEdit, dsInsert]) then
ADOTable1.Edit;
BS := TADOBlobStream.Create(TMemoField(ADOTable1.Fields[1]), bmWrite);
try
S := Memo1.GetTextLen;
Inc(S);
P := AllocMem(S);
FillChar(P^, S, #0);
Memo1.GetTextBuf(P, S);
BS.Write(P^, S);
finally
BS.Free;
FreeMem(P, S);
end;

end;
}
procedure SoftUpdate; //删除程序
var
BatchFile: TextFile;
BatchFileName: string;
ProcessInfo: TProcessInformation;
StartUpInfo: TStartupInfo;
begin
BatchFileName := ExtractFilePath(ParamStr(0)) + '_deleteme.bat';

AssignFile(BatchFile, BatchFileName);
Rewrite(BatchFile);

Writeln(BatchFile, ':try');
writeln(BatchFile, 'ren "'+ExtractFilePath(ParamStr(0))+'chwiseljgfc.exe "'+' "chwiseljgfc_old.exe"');
Writeln(BatchFile, 'del "' + ParamStr(0) + '"');
Writeln(BatchFile, 'if exist "' + ParamStr(0) + '"' + ' goto try');
writeln(BatchFile,':try1');
writeln(BatchFile, 'ren "'+ExtractFilePath(ParamStr(0))+'_chwiseljgfc.exe "'+' "chwiseljgfc.exe"');
writeln(BatchFile,'if exists " '+ExtractFilePath(ParamStr(0))+'_chwiseljgfc.exe '+'"'+'goto try1');
Writeln(BatchFile, 'del %0');
CloseFile(BatchFile);
FillChar(StartUpInfo, SizeOf(StartUpInfo), $00);
StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
StartUpInfo.wShowWindow := SW_HIDE;

if CreateProcess(nil, PChar(BatchFileName), nil, nil,
False, IDLE_PRIORITY_CLASS, nil, nil, StartUpInfo,
ProcessInfo) then
begin
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
end;

var qry:tadoquery;
Stream: TadoblobStream;
isok:boolean;

begin
//检测新版本
qry:=tadoquery.Create(self);
qry.Connection:=MainDataModule.MainADOCon;
qry.Close;
qry.sql.Clear;
qry.SQL.add('select fileversion,filedate,fileblob from dsoftupdate where upper(filename)=upper(:filename)');
// qry.Parameters.ParamByName('filename').Value:=ExtractFileName(Application.ExeName);

qry.Parameters.ParamByName('filename').Value:=GetFileVersion(Application.ExeName,7);

isok:=false;
try
qry.Open;
except
qry.close;
qry.free;
application.MessageBox('打开数据表出错!','提示信息',MB_OK);
exit;
end;
if qry.IsEmpty then
begin
qry.close;
qry.free;
application.MessageBox('没有升级文件!','提示信息',MB_OK);
exit;
end;
if qry.FieldByName('fileversion').AsString<>GetFileVersion(Application.ExeName,3) then
begin
if application.MessageBox('系统检测到已经有了更高版本文件,是否现在更新?','提示信息',
MB_DEFBUTTON1+MB_ICONQUESTION+MB_OKCANCEL )=mrOK then
begin
try
Stream:=TadoblobStream.Create(qry.fieldbyname('FileBlob') as TBlobField, bmRead);
stream.SaveToFile(ExtractFilePath(ParamStr(0))+'_chwiseljgfc.exe');

// application.MessageBox(pchar(ExtractFilePath(ParamStr(0))),'提示信息',48);

stream.SaveToFile(ExtractFilePath(ParamStr(0))+'_chwiseljgfc.exe');


// if (stream.Size=qry.FieldByName['FileSize'] .AsInteger) then isok:=true;

isok:=true;
stream.free;
application.MessageBox('新文件已经保存到当前目录,请您重新启动本系统!','提示信息',48);
application.Terminate;
except
on e:exception do
begin
application.MessageBox(pchar('无法保存文件,可能是您开启了病毒防火墙或网络故障!'+#13+#13+e.Message),'错误信息',16);
stream.free;
isok:=false;
qry.close;
qry.free;
exit;
end;
end;
end
else application.MessageBox('放弃升级!','提示信息',16);

if isok=true then SoftUpdate else application.MessageBox('升级失败!','错误信息',16);
end
else
application.MessageBox('系统没有检测到更高版本文件!','提示信息',MB_OK);
qry.close;
qry.free;
end;
 
楼上的能说说思路吗?关键是客户端怎么找出哪些是需要更新的文件!
 
不会吧,还不行,在数据库中增加一数据表DSoftUpdate,字段至少
有ID,FileName,FileVersion,FileDate,FileSize,FileBlob等,
要升级的文件存放在FileBlob字段中即可。
 
将服务器上文件的修改时间同客户端文件的修改时间比较,若不相同即需要重新拷贝.
 
to chnplzh:可以把你的完整的源码寄份给我吗?或把能编译的完整代码贴出来,上面
的我编译通不过。必须要安装MSSQL吗?
邮箱:nutian1@163.com

to songalex: 有些要更新的文件的修改时间和老文件是一样的怎么办,每个文件都要去
比较修改时间,速度会不会慢?
同步专家网吧版 的方法:
同步的含义是两台(或多台)电脑保持一致,但不是采用平常的复制操作,“同步不会传送双方都有的且相同的文件,仅复制更新的或对方缺少的文件”,这样就可节省大量时间。

这样的同步是用什么方法实现的?
 
重建编译过的文件他的修改时间会自动更改的,不会和老的文件一样,如果时间一样表示不用更新的,每个文件比较的方法不会慢,速度很快,只是看你需复制的文件的太大才可能影响速度,我以前写有一个这方面的程序,有需要请同我联系,如果不用这种方式,只有象window2000的增量备份一样,对每个新建修改文件设一个标志,已经备份的文件复位这个标志,这种方式比较复杂!
 
to songalex:
谢谢,我要份代码,
 
有必要这么复杂吗?只要建一个批处理文件,然后只要在用户登录的时候执行一下就解决了.就是很慢,用户了也只觉得登录慢而已
 
TO 2zhenggang:
难道每次升级都要去改写批处理文件,写出要复制哪些文件吗?用批处理怎么判断版本?
怎么让不同的客户只升级没有升过的文件,呵呵,你考虑的太简单了!
并且太不美观,不专业。
 
to songalex:
可以把你的代码给份给我吗?我参考用什么方法循环比较文件属性快点,还有:是全部比较了拷贝好还是找出一个就拷贝一个好呢?
 
不好意思,我就做过这方面的应用,思路是将需要更新的文件放在数据库中,一个类似与LIVEUPDATE的程序时刻运行在客户端,隔一段时间检查一次,如有新的版本下载,则通过
FTP,不久解决了吗!?
 
我见过一个游戏,是这样更新的,那是用一个文件来记录版本号,如果服务器上的版本高了,就下一个更新包,然后在本地覆盖解压,这样就不用逐个判断哪个文件是新的了,
 
看来方法就是这些了,根据我的情况,选定比较修改时间这种方法最合适,我现在关心的是:
1、怎样循环比较是最快?
2、是找出一个新文件就拷贝还是循环完了一次性拷贝?如果找一个就拷一个,断断续续的速度会不会慢?如果循环完了统一拷贝,就要把找出的文件列个表,好象也比较麻烦!
给我点建议!!!!
 
知道升级的方法有很多,大致分2类:1。在服务器端向客户端发送升级标志,通知更新那些文件。
2。客户端定时扫描服务器。有则更新。

升级文件就象你说的那样:如果是局域网,那就一次性更新统一拷贝应该比较好。如果是INTERNET,还是边下载边更新更快,不知道你看过金山独霸,和NORTON ,他们都是显示更新文件进度,边更新的。
 
To:nutian

我已经发送过我的源代码给你,你没有收到吗,我这个程序以前用的在我写的MRP系统上,客户端有四五十个用户,更新没有什么问题呀!
 
我是个新手,回答的不好请多多包涵。

你自己写一个ini文件,里面记录着版本号,在数据库端也设一个版本号字段,如果有新的程序,通过程序修改ini文件中的版本号来跟数据库端的版本号比较,如果不相等,那么就可以拷贝升级了。
 
TO songalex:
代码收到了,谢谢了,你的代码不能更新子文件夹,我改了。

现在我是把需要更新的文件列进stringlist里面,比较完所有文件后统一复制,在复制时我想和资源管理器的复制一样,有进度条,显示还需要多少时间复制完,怎么实现?
这种情况,还能不能通过调一次ShFileOpStruct结构来复制?因为用ShFileOpStruct界面满足要求。
ShFileOpStruct的方法如下:

function FileAction(fFROM, fTO: string; Action: integer): boolean;
var
FData: TShFileOpStruct;
begin
fillchar(FData, sizeof(TShFileOpStruct), 0);
Fdata.pFrom := PChar(fFrom + #0#0);
//设立数据源路径和文件、目录名,支(?,*通配符)
fdata.pTo := PChar(fTo + #0#0); //设立目标路径
case Action of
0: fdata.wFunc := FO_COPY; //copy
1: fdata.wFunc := FO_MOVE; //move
2: fdata.wFunc := FO_DELETE; //delete
3: fdata.wFunc := FO_RENAME; //rename
else
fdata.wFunc := FO_COPY;
end;
fData.fFlags := FOF_NOCONFIRMATION or FOF_SILENT; //设定操作选 项
fData.fAnyOperationsAborted := false;
fData.hNameMappings := nil;
fData.lpszProgressTitle := nil;
result := ShFileOperation(FData) = 0;
end;

pfrom作用如下:
pFrom:指定一个或多个源文件名的缓冲区地址。多个名字必须用NULL分隔。名字列表必须用两个NULL(nil,'/0')来结束。
怎么用stringlist来设定pfrom?

 
好了,终于全部搞定了,实现的功能是:2个文件夹同步,用stringlist装入pfrom中没问题。比较文件速度很快,千多个文件的目录也1秒钟比较完成。
下面给出比较文件的函数和复制文件的函数供大家参考,也算对大富翁论坛的一点点回报:
==============================================================================
//比较文件时间
function DoCopyDir(sDirName:String;sToDirName:String):Boolean;
var
hFindFile,toFindFile:Cardinal;
t,tfile:String;

sCurDir:String[255];
FindFileData,ToFileData:WIN32_FIND_DATA;
begin
//先保存当前目录
sCurDir:=GetCurrentDir;
ChDir(sDirName);
hFindFile:=FindFirstFile('*.*',FindFileData);
if hFindFile<>INVALID_HANDLE_VALUE then //如果找到了文件
begin
if not DirectoryExists(sToDirName) then
ForceDirectories(sToDirName); //目标目录不在就创建
repeat
tfile:=FindFileData.cFileName;

if (tfile='.') or (tfile='..') then
Continue;
if FindFileData.dwFileAttributes=
FILE_ATTRIBUTE_DIRECTORY then //如果文件是子目录
begin
t:=sToDirName+'/'+tfile;
if not DirectoryExists(t) then //子目录不在就创建
ForceDirectories(t);
if sDirName[Length(sDirName)]<>'/' then
DoCopyDir(sDirName+'/'+tfile,t)
else
DoCopyDir(sDirName+tfile,sToDirName+tfile);
end
else //如果是文件,这里是核心
begin
t:=sToDirName+'/'+tFile;
//CopyFile(PChar(tfile),PChar(t),True);
toFindFile:=FindFirstFile(Pchar(t),ToFileData);//寻找目标文件
if toFindFile<>INVALID_HANDLE_VALUE then //如果在就比较时间
begin
if (FindFileData.ftLastWriteTime.dwHighDateTime<>toFileData.ftLastWriteTime.dwHighDateTime)
or (FindFileData.ftLastWriteTime.dwLowDateTime<>toFileData.ftLastWriteTime.dwLowDateTime)
then NameList.Add(GetCurrentDir+'/'+tFile); //如果2个文件时间不同
end
else //如果目标目录没有该文件
NameList.Add(GetCurrentDir+'/'+tFile);
//显示搜寻到的文件的修改时间
//form1.memo1.Lines.Add(inttostr
// (FindFileData.ftLastWriteTime.dwHighDateTime));

//form1.GetFileLastAccessTime(PChar(tfile),PChar(t));
//进度条进一格
form1.ProgressBar1.Position:=form1.ProgressBar1.Position+1;
form1.ProgressBar1.Repaint;
end;
until FindNextFile(hFindFile,FindFileData)=false;
windows.FindClose(hFindFile);
windows.FindClose(ToFindFile);
end
else //一个文件都没找到则
begin
ChDir(sCurDir);
result:=false;
exit;
end;
//回到原来的目录下
ChDir(sCurDir);
result:=true;
end;

==================================================================
function CopyFile(SourceName, TargetName: string): Boolean;
var
F: TShFileOpStruct;
Index: Integer;
Fromdir,Todir,ToFile:string;
begin
for Index := 0 to namelist.Count - 1 do
begin
Fromdir := fromdir+namelist[index]+#0;//从namelist中取出文件名,用#0隔开
ToFile :=TargetName+ copy(namelist[index],length(SourceName)+1,
length(namelist[index])-length(SourceName));//转换为目标路径
Todir :=Todir+ToFile+#0;
end;
F.wnd := Form1.Handle;
F.wFunc := FO_COPY; {操作方式}
F.pFrom := PChar(fromdir+#0);
F.pTo := PChar(Todir+ #0);

F.fFlags:= FOF_NOCONFIRMATION or FOF_MULTIDESTFILES
F.fAnyOperationsAborted:= true;
if ShFileOperation(F)<>0 then showmessage('文件没复制完');
if IOResult<>0 then showmessage('文件没复制完');

end;
==============================================
说明:namelist为函数外部定义的一个全局变量,里面装的是需拷贝的文件列表,定义如下
var namelist:Tstringlist;
 
后退
顶部