-
-
[原创]电子教室软件开发之广播文件
-
发表于: 2011-3-10 18:45 4029
-
小弟刚学编程,近段时间开发《电子教室软件》(类似于NetOp School等等这样的软件)
很多都是独立想出来的思路,特一篇一篇的分享。由于编程不在行,希望高手指点。
用delphi的indy中的TIdTCPServer(教师端)、IdTCPClient(学生端)可以向所有学生广播文件和目录。
主要思路如下:
学生机首先得到文件目录列表,
然后学生机根据这个目录列表一个一个的向教师请求并下载。
假定要向50个学生机器广播文件目录,教师要对每个连接进行处理。
第一,广播文件目录时,教师端首先将要广播的文件目录存到一个文件bfzx.txt里
procedure Tfrm_File_Gb.SpeedButton5Click(Sender: TObject);
var //发送按钮
i,j: Integer;
k:Integer;//计算文件总个数
m,mm:real;//计算所有文件大小
Cmd: TCtlCmd;
txt:TStringList;
begin
ButtonBegin;//只有取消有效
txt := TstringList.Create; //将listbox1里的内容存成 txt文件
label2.Caption :='正在计算大小......';
IdTCPServer1.DefaultPort := bfzxport - 3;
try
if not IdTCPServer1.Active then IdTCPServer1.Active := True;
except
showmessage('端口绑定错,请关主程序后再启动');
end;
//-----------------第一步 将list2里的列表和大小计算出来
k:= 0; //文件总个数
m:= 0; //发送的文件总大小
for i:=ListView2.Items.Count-1 downto 0 Do
begin
if ListView2.Items.Item[i].ImageIndex = 0 then //第一,如果是目录 得到目录列表
begin
//--------------------
if wllstop > 1 then
begin
listview2.Clear;
listbox1.Clear;
txt.Clear;
ButtonEnd;
exit;
end;
application.ProcessMessages;
//--------------------
//目录个数 与 > 代表这一段数据是 某目录下的。
listbox1.Items.Add(ListView2.Items[i].Caption + '>' + inttostr(MakeFileList(ListView2.Items[i].Caption,'.*').Count));
for j:=MakeFileList(ListView2.Items[i].Caption,'.*').Count -1 downto 0 Do
begin
listbox1.Items.Add(MakeFileList(ListView2.Items[i].Caption,'.*')[j]);
k:=k+1;
m := m + GetFileSize1( MakeFileList(ListView2.Items[i].Caption,'.*')[j]);
//a处
//--------------------
if wllstop > 1 then
begin
listview2.Clear;
listbox1.Clear;
ButtonEnd;
txt.Clear ;
exit;
end;
application.ProcessMessages ;
//--------------------
mm := (m/1048576) ;
label2.Caption := '正在计算发送的总个数:[' + inttostr(k) + '] 总大小:' + Format('%.2f', [mm]) + 'M字节';
if k > 180 then
begin
showmessage('测试版,发送的总文件个数多于180个了,不能发送。');
listview2.Clear;
listbox1.Clear;
ButtonEnd;
exit;
end;
if mm > 199 then //大于100M不能发送
begin
showmessage('测试版,发送的总文件的总大小大于200M了,不能发送。');
listbox1.Clear;
exit;
end;
//a处end
end;
end
else //第二,是文件
begin
listbox1.Items.Add(ListView2.Items[i].Caption + '**');//单个文件用**来标志
k:=k+1;
m := m + GetFileSize1(ListView2.Items[i].Caption);
//a处
//--------------------
if wllstop > 1 then
begin
listview2.Clear;
listbox1.Clear;
ButtonEnd;
txt.Clear;
exit;
end;
application.ProcessMessages;
//--------------------
mm := (m/1048576) ;
label2.Caption := '正在计算发送的总个数:[' + inttostr(k) + '] 总大小:' + Format('%.2f', [mm]) + 'M字节';
if k > 180 then
begin
showmessage('测试版,发送的总文件个数多于180个了,不能发送。');
listview2.Clear;
listbox1.Clear;
ButtonEnd;
exit;
end;
if mm > 199 then //大于100M不能发送
begin
showmessage('测试版,发送的总文件的总大小大于200M了,不能发送。');
listview2.Clear;
listbox1.Clear;
ButtonEnd;
exit;
end;
//a处end
end;
//b处
label2.Width := 450;
label2.Caption := '发送的文件总个数:[' + inttostr(k) + '] 总大小:' + Format('%.2f', [mm]) + 'M字节';
//b处end
end;
//最后一项写上保存的位置
listbox1.Items.Add(Combobox1.Text);
txt := tstringlist(listbox1.Items);
txt.SaveToFile(ExtractFilePath(Application.ExeName) + '\bfzx.txt');
//上面将选择的所有都放到了listbox里面
// 学生得到后,如果后面打问号的,则建立
//-----------------第二步
//当点击发送按钮时,则处于active状态,且发送 28号指令
ButtonBegin; // 只有取消按钮可用
Cmd.Cmd := 28; //trans File
for i := 0 to frmMain.lv1.Items.Count - 1 do
begin
if frmMain.lv1.Items.Item[i].Selected then
begin
try
TMyClient(frmMain.lv1.Items.Item[i].SubItems.Objects[0]).Send(@Cmd, SizeOf(Cmd));
except
end;
end;
end;
//----------------- 第三步
end;
第二:教师端处理学生的请求如下:
procedure Tfrm_File_Gb.IdTCPServer1Execute(AThread: TIdPeerThread);
var
s, sCommand,sAction : string;
fStream : TFileStream;
tBM : tbitmap;
//以下为 PIC:时用
cmd: string; //接收到客户端的字符串信息
ASize: Integer; //需要传输的流大小
AFileStream: TFileStream; //传输的文件流
ss_1 : string;
i:integer;
begin
CS.Enter; //进程互斥
try
//########################
//读来的指令有 LST: PIC:c:\wll\22.doc END 0 32768 ... 等等//########################
//AFileStream:=tfilestream.Create('E:\Media\维尼和跳跳虎_维尼和跳跳虎01屹耳掉了尾巴_黑暗里的亮光.rmvb',fmOpenRead + fmShareDenyNone);
s := uppercase(AThread.Connection.ReadLn); //如果咱路径太长。。。。。。。怎么办?
syn_kk := s;
AThread.Synchronize(syn_bfzx); //同步d
//label2.Caption := copy(s,0,50);
//Application.ProcessMessages;
//memo1.Lines.Add(s);
sCommand := copy(s,1,3);
sAction := copy(s,5,length(s)-4 ); //从第五个字符开始
if sCommand = 'LST' then
begin
//---------------------------begin
if FileExists( ExtractFilePath(Application.ExeName) + '\bfzx.txt') then //将文件列表txt发送过去
Begin
//lstRequests.items.add('Serving up: ' + sAction);
// open file stream to image requested
fStream := TFileStream.Create(ExtractFilePath(Application.ExeName) + '\bfzx.txt',fmOpenRead + fmShareDenyNone);
// copy file stream to write stream
AThread.Connection.OpenWriteBuffer;
AThread.Connection.WriteStream(fStream);
AThread.Connection.CloseWriteBuffer;
// free the file stream
FreeAndNil(fStream);
//lstRequests.items.add('File transfer completed');
//-----------(((((((
syn_pp := AThread.Connection.Socket.Binding.PeerIP;
AThread.Synchronize(syn_1); //同步d
//-----------(((((((
End
else
AThread.Connection.WriteLn('ERR - bfzx.txt文件不存在');
AThread.Connection.Disconnect;
//---------------------------end
end
else
if sCommand = 'END' then
begin
//释放文件流
//StatusBar1.SimpleText := '传输完成...';
//===同步e====ProgressBar1.Position := 0;
syn_oo := AThread.Connection.Socket.Binding.PeerIP;
AThread.Synchronize(syn_2); //同步d
//=======Application.ProcessMessages;
AThread.Connection.Disconnect; //该连接断开
End
else
if sCommand = 'OVE' then
begin
//----------------(((((
//syn_mm := '完成!';
AThread.Synchronize(syn_3);
//----------------(((((
AThread.Connection.Disconnect; //该连接断开
End
else
if sCommand = 'PIC' then
begin
//----------------
//首先使 文件流变量关连上某文件
AFileStream:=tfilestream.Create(sAction,fmOpenRead + fmShareDenyNone);
//第一步:告诉大小
AThread.Connection.WriteLn(Format('%d', [AFileStream.Size]));
//--frm_File_Gb.StatusBar1.SimpleText := '准备传输...';
//=====ProgressBar1.Max := AFileStream.Size; //总大小
//=====同步f====Application.ProcessMessages;
AFileStream.Free;
//----------------
end
else
if pos('|',s) > 1 then
begin
//########发来的是 32768|e:\大矸\23.doc 的形式
AFileStream:=tfilestream.Create(Copy(s, Pos('|', s) + 1, Length(s)),fmOpenRead + fmShareDenyNone);
//Copy(s, Pos('|', s) + 1, Length(s))
//下面是s 切记 切记
try
StrToInt(Copy(s, 0, Pos('|', s) - 1));//总得判断下cmd是不是字符
except
Exit;
end;
AFileStream.Seek(StrToInt(Copy(s, 0, Pos('|', s) - 1)), soFromBeginning); //转到文件流传输的位置
ASize := Min(AFileStream.Size - AFileStream.Position, AThread.Connection.RecvBufferSize);
//计算需要发送的大小,Min()函数在Math单元
AThread.Connection.OpenWriteBuffer; //准备发送缓冲
AThread.Connection.WriteStream(AFileStream, false, false, ASize);
//注意这个函数的参数。
AThread.Connection.CloseWriteBuffer; //结束发送缓冲
//====ProgressBar1.Position := ProgressBar1.Position + ASize;
//====同步h=====Application.ProcessMessages;
AFileStream.Free;//?放于此???
end;
except
on E : Exception do
ShowMessage(E.Message);
End;
CS.Leave;
end;
第三、学生端的程序如下(这是一个线程):
unit MyFile2;
interface
uses
//--------------------1
Classes,
Windows, Messages, SysUtils, Variants, Graphics, Controls, Forms,
Dialogs,
Math,
ShellApi, ShlObj, //获取桌面的路径
StdCtrls,//下面的listbox要用
bfzx_pub,
ScreenSpy,//用来发28号命令
//Main,//下面的 listbox1要用到?
IdTCPConnection, IdTCPClient;
//---------------------1 end
type
TMyFile2 = class(TThread)
private
//--------------------2
txt:TStringList; //只要取这城面的每一项就可以下载下载了。
IdTCPClient: TIdTCPClient;
FHost: string; //记录教师方的IP
fn_wll: string; //文件名
cmd_1: string;
kk : integer;
ASize, TotalSize: Int64;
AFileStream: TFileStream;
function GetSpecialFolderDir(const folderid: integer): string;
//--------------------2 end
{ Private declarations }
protected
procedure Execute; override;
//---------------------3
public
constructor Create; reintroduce;
destructor Destroy; override;
property Host: string read FHost write FHost; //远方的IP地址
//---------------------3 end
end;
implementation
{ Important: Methods and properties of objects in visual components can only be
used in a method called using Synchronize, for example,
Synchronize(UpdateCaption);
and UpdateCaption could look like,
procedure TMyFile2.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }
{ TMyFile2 }
//--------------------5 //自己加的 Create函数
constructor TMyFile2.Create;
begin
FreeOnTerminate := True; //表示执行完会自动释放掉所有的
IdTCPClient := TIdTCPClient.Create(nil);
txt := TStringList.Create;
inherited Create(True);
end;
destructor TMyFile2.Destroy; //是不是等价于窗体的 Destroy 事件?
begin
//当线程终止时,End 指令对吗?
//IdTCPClient1.WriteLn('CANCEL'); //提示服务器传输完成
//IdTCPClient1.Disconnect; //断开连接
IdTCPClient.Free;
inherited;
end;
function TMyFile2.GetSpecialFolderDir(const folderid: integer): string;
var
pidl: pItemIDList;
buffer: array[0..255] of char;
begin
//取指定的文件夹项目表
SHGetSpecialFolderLocation(application.Handle, folderid, pidl);
SHGetPathFromIDList(pidl, buffer); //转换成文件系统的路径
Result := strpas(buffer);
end;
//--------------------5 end
procedure TMyFile2.Execute;
var
ftmpStream : TFileStream;//接收文件流
i,j: integer;
s_1,s_2,s_3 : String;
kk:integer;
y_1,y_2,y_3 : String;
dir : String;
//---------第二种方式要用的变量
cmd: string;
ASize, TotalSize: Int64;
AFileStream: TFileStream;
//---------第二种方式要用的变量结束
begin
{ Place thread code here }
//--------------------444444
//----------------------999
try
with IdTCPClient do
begin
if connected then DisConnect;
Host := FHost;//赋值 远端的ip地址
Port := bfzxport - 3;
Connect; //★★★★★★begin下述将文件列表保存为bfzx1.txt
WriteLn('LST:');
// delete if exists
// in production situation you might store binary downloads like this in a cache folder
if FileExists(ExtractFileDir(ParamStr(0)) + '\bfzx1.txt') then
DeleteFile(ExtractFileDir(ParamStr(0)) + '\bfzx1.txt');
ftmpStream := TFileStream.Create(ExtractFileDir(ParamStr(0)) + '\bfzx1.txt',fmCreate);
while connected do
ReadStream(fTmpStream,-1,true);
FreeAndNil(fTmpStream);
txt.LoadFromFile(ExtractFilePath(Application.ExeName) + '\bfzx1.txt');
//txt := tstringlist(listbox1.Items);
//listbox1.Items := txt;
//for j:=txt.Count -1 downto 0 Do
//begin
// showmessage(txt[j]);
//end;
Disconnect; //★★★★★★end
end;
except
on E : Exception do
////9999 ShowMessage(E.Message);
end;
//############################################1 begin
//第一步 将txt[txt.Count-1]里的值取出来建立文件夹
//==showmessage(txt[txt.Count-1]);
if txt[txt.Count-1] = '学生机的桌面' then
begin
dir:=GetSpecialFolderDir(0) ;
end
else if txt[txt.Count-1] = '学生机的桌面\学生素材\' then
begin
//--------
dir:=GetSpecialFolderDir(0) + '\学生素材';
if not DirectoryExists(dir) then //先判断文件夹在不在,不在再创建
begin
try
ForceDirectories(dir); //返回 Boolean
except
showmessage('创建[学生素材]文件夹失败,接收文件失败。');
exit;
end;
end;
//--------
end
else if txt[txt.Count-1] = '学生机的桌面\百宝箱\' then
begin
//--------
dir:=GetSpecialFolderDir(0) + '\百宝箱';
if not DirectoryExists(dir) then //先判断文件夹在不在,不在再创建
begin
try
ForceDirectories(dir);
except
showmessage('创建[百宝箱]文件夹失败,接收文件失败。');
exit;
end;
end;
//--------
end
else
begin
//--------
dir := txt[txt.Count-1];
if not DirectoryExists(dir) then //包括其子目录一块创建
if not ForceDirectories(dir) then
showmessage('创建文件夹失败,接收文件失败。' + dir );
//--------
end;
//############################################1 end
//############################################2 begin
//上述得到 dir 即保存位置 ,不以\结束
//==showmessage(dir);
//第二步,从txt[]数组里一个一个的下载文件,是目录的要创建目录
i := 0;
While( i < txt.Count-1 ) do //只处理总数少数1个元素,因为最后一个元素为 存放位置
begin
if pos('**',txt[i]) > 2 then //有两星的为文件
begin
//直接下载
//showmessage(txt[i]); //显示原字符串
s_1 := copy(txt[i],0,pos('**',txt[i]) -1 );
//showmessage(s_1); //去掉两星
s_2 := ExtractFileDir(s_1); //得到目录名 不以\结尾
//showmessage(s_2);
s_3 := ExtractFileName(s_1); //得到文件名
//showmessage(s_3);
//===============================begin
Try
//with IdTCPClient do
//begin
if IdTCPClient.connected then IdTCPClient.DisConnect;
IdTCPClient.Host := FHost;//赋值 远端的ip地址
IdTCPClient.Port := bfzxport - 3;
IdTCPClient.Connect; //★★★★★ 发PIC:指令是发起新的连接 ★★★★★★★★★
IdTCPClient.WriteLn('PIC:' + s_1 );
cmd := IdTCPClient.ReadLn; //这里是获取大小
//以"|"符号分离文件名
//showmessage(cmd);
//TotalSize := StrToInt64(Copy(cmd, 0, Pos('|', cmd) - 1)); //分离文件总大小 。。。总的好不好。。
TotalSize := StrToInt(cmd); //分离文件大小
//建立文件流准备接收
AFileStream := TFileStream.Create(Dir + '\' + s_3, fmCreate);
try //循环开始接受
repeat
//WriteLn(Format('%d|%s', [AFileStream.Size, ExtractFileName(Edit1.Text)]));
//IdTCPClient.WriteLn(IntToStr(AFileStream.Size));//发送当前传输的位置
IdTCPClient.WriteLn(Format('%d|%s', [AFileStream.Size, s_1]));
ASize := Min(TotalSize - AFileStream.Size, IdTCPClient.RecvBufferSize);
//选择剩余大小和缓冲区大小小的一个作为传输的大小
IdTCPClient.ReadStream(AFileStream, ASize); //接收流
//StatusBar1.SimpleText := Format('当前传输位置%d/大小%d', [AFileStream.Size, TotalSize]);
Application.ProcessMessages;
until AFileStream.Size >= TotalSize; //大小一致了表示结束
finally
AFileStream.Free; //释放文件流
end;
IdTCPClient.WriteLn('END:'); //提示服务器传输完成
IdTCPClient.Disconnect; //★★★★★
except
on E : Exception do
///9999ShowMessage(E.Message);
end;
//===============================end
i:= i+1;
continue;
end
else if ( pos('>',txt[i]) > 2 ) then //只有>的情况 ,表示是传的文件夹
begin
//###202020begin
s_1 := copy(txt[i],0,pos('>',txt[i]) -1 ); //E:\大矸成人学校\三门>22
//showmessage(s_1); //去掉>22
//s_1 := copy(extractfiledir(s_1),0,length(extractfiledir(s_1))-1);
s_1 := ExcludeTrailingPathDelimiter(extractfiledir(s_1)); //先去掉后面的,再无论有无,先去掉 \
kk := strtoint(Copy(txt[i], Pos('>', txt[i]) + 1, Length(txt[i]))); //得到数目
//showmessage(inttostr(kk));
//Application.ProcessMessages;
i:=i+1; //##############
//s_3 := ExtractFileName(s_1); //得到文件名
//showmessage(s_3);
for j := 0 to kk - 1 do
begin
if ( pos('?',txt[i]) > 2 ) then
begin
//++++++++++++1
y_1 := copy(txt[i],0,pos('?',txt[i]) -1 ); //去掉 ?
//y_1 := extractfiledir(y_1);
y_2 :=copy(y_1,pos(s_1,y_1)+ length(s_1),length(y_1)); //+ length(s_1)
y_3 := dir + y_2 ;
if not DirectoryExists(y_3) then
try
ForceDirectories(y_3);
except
end;
i := i+1; //##############$$$
//++++++++++++1 end
end
else
begin
//++++++++++++2
s_2 := ExtractFileDir(txt[i]); //得到目录名 不以\结尾
y_2 :=copy(s_2,pos(s_1,s_2)+ length(s_1),length(s_2)); //+ length(s_1)
//y_2 := copy(y_2,0,RightPos('\',y_2));
y_3 := dir + y_2 ;
if not DirectoryExists(y_3) then
try
ForceDirectories(y_3);
except
end;
// 真正要保存的位置是 y_3 + '\' + ExtractFileName(txt[i])
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%begin
//===============================begin
Try
//with IdTCPClient do
//begin
if IdTCPClient.connected then IdTCPClient.DisConnect;
IdTCPClient.Host := FHost;//赋值 远端的ip地址
IdTCPClient.Port := bfzxport - 3;
IdTCPClient.Connect; //★★★★★ 发PIC:指令是发起新的连接 ★★★★★★★★★
IdTCPClient.WriteLn('PIC:' + txt[i] );
cmd := IdTCPClient.ReadLn; //这里是获取大小
//以"|"符号分离文件名
//showmessage(cmd);
//TotalSize := StrToInt64(Copy(cmd, 0, Pos('|', cmd) - 1)); //分离文件总大小 。。。总的好不好。。
TotalSize := StrToInt(cmd); //分离文件大小
//建立文件流准备接收
AFileStream := TFileStream.Create(y_3 + '\' + ExtractFileName(txt[i]), fmCreate);
try //循环开始接受
repeat
//WriteLn(Format('%d|%s', [AFileStream.Size, ExtractFileName(Edit1.Text)]));
//IdTCPClient.WriteLn(IntToStr(AFileStream.Size));//发送当前传输的位置
IdTCPClient.WriteLn(Format('%d|%s', [AFileStream.Size, txt[i]]));
ASize := Min(TotalSize - AFileStream.Size, IdTCPClient.RecvBufferSize);
//选择剩余大小和缓冲区大小小的一个作为传输的大小
IdTCPClient.ReadStream(AFileStream, ASize); //接收流
//StatusBar1.SimpleText := Format('当前传输位置%d/大小%d', [AFileStream.Size, TotalSize]);
Application.ProcessMessages;
until AFileStream.Size >= TotalSize; //大小一致了表示结束
finally
AFileStream.Free; //释放文件流
end;
IdTCPClient.WriteLn('END:'); //提示服务器传输完成
IdTCPClient.Disconnect; //★★★★★
except
on E : Exception do
//99999999 ShowMessage(E.Message);
end;
//===============================end
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%end
i := i+1; //##############$$$
//++++++++++++2 end
end;
end;
//###202020end
end;
end;
//############################################2 end
//----------------------999 end
//-----第三步发传完指令--------begin
try
with IdTCPClient do
begin
if connected then DisConnect;
Host := FHost;//赋值 远端的ip地址
Port := bfzxport - 3;
Connect; //★★★★★★begin下述将文件列表保存为bfzx1.txt
WriteLn('OVE:');
Disconnect; //★★★★★★end
end;
except
on E : Exception do
////9999 ShowMessage(E.Message);
end;
//-----第三步发传完指令--------end
//--------------------444444 end
end;
end.
第四,学生端在接收到指令时才开始接收文件
28: //接收教师发来的28号指令
begin
Filedown2 := TMyFile2.Create;
Filedown2.Host := Teacher_Ip;
Filedown2.Resume;
end;
第五、广播的文件列表样式如下
末尾说明:
打双星的为文件;24表示此目录下有多少个;打?的表示是目录
F:\I\Old\BOOTEX.LOG**
E:\大矸成人学校>24
E:\大矸成人学校\三门\管理信息系统.doc
E:\大矸成人学校\三门\电子商务物流管理\答案.doc
E:\大矸成人学校\三门\电子商务物流管理\电子商务物流管理考试题.doc
E:\大矸成人学校\三门\电子商务物流管理\?
E:\大矸成人学校\三门\电子商务物流管理?
E:\大矸成人学校\三门\电子商务安全\电子商务信息安全B卷答案.doc
E:\大矸成人学校\三门\电子商务安全\电子商务信息安全B卷.doc
E:\大矸成人学校\三门\电子商务安全\电子商务信息安全A卷答案.doc
E:\大矸成人学校\三门\电子商务安全\电子商务信息安全A卷.doc
E:\大矸成人学校\三门\电子商务安全\?
E:\大矸成人学校\三门\电子商务安全?
E:\大矸成人学校\三门\电子商务信息安全.doc
E:\大矸成人学校\三门\bb\2\?
E:\大矸成人学校\三门\bb\2?
E:\大矸成人学校\三门\bb\1\?
E:\大矸成人学校\三门\bb\1?
E:\大矸成人学校\三门\bb\?
E:\大矸成人学校\三门\bb?
E:\大矸成人学校\三门\AcroRd32.exe.lnk
E:\大矸成人学校\三门\aa\?
E:\大矸成人学校\三门\aa?
E:\大矸成人学校\三门\?
E:\大矸成人学校\三门?
E:\大矸成人学校\?
学生机的桌面\学生素材\
第六、上述程序代码是原样拷贝,测试通过,但没排版,请见量。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!