使用 VC 构建安全私有文件共享的客户端/服务器应用程序






3.46/5 (9投票s)
2005 年 11 月 10 日
9分钟阅读

61597

2842
如何使用免费 SDK 构建安全的私有文件共享客户端/服务器
引言
如今,大多数加密文件共享解决方案都使用数字证书,这些证书适用于用户基数庞大且事先未知的应用程序。但是,如果您想避免获取和维护证书的成本和麻烦,还有另一种可能的解决方案。在本文中,我们介绍了一个用于安全私有文件共享的简单客户端/服务器解决方案。安全性通过对称加密算法(AES 128)和共享密钥来实现。在已建立小型群组的许多情况下,这可能更有效且更实用。
开始之前
下载并安装 文件共享 SDK 的免费版本,该版本可免费用于非商业用途。客户端/服务器组件采用 128 位 AES 加密,无需任何 SSL 证书。相反,它使用共享密钥,创建一个基于应用程序的虚拟专用网络。除了标准的 FTP 操作(如上传、下载、重命名、删除等)之外,该库还提供了一系列高级功能,例如在服务器上打开可查找流、远程搜索文件和文本、远程 zip 压缩和解压缩。
服务器
加载示例项目 _FileServer.sln_。可以通过控件变量 _c_FileSrv_ 访问服务器组件。让我们回顾一下它的主要属性和方法。
启动服务器
为了开始接受连接,我们必须执行以下步骤:
- 将属性 _ListenningPort_ 设置为监听端口号。我们从设置窗体获取该值。在本例中,默认值为 _2345_。
- 如果我们要建立加密连接,我们必须将属性 _SecurityMode_ 设置为 _2_,并将 _SecretKey_ 设置为设置窗体上指定的加密密钥的值。如果我们不需要加密,则必须将 _SecurityMode_ 设置为 _0_。
- 如果我们要使用数据包压缩,则必须将 _UseCompression_ 设置为 _TRUE_。
- 调用方法 _Start()_。
//Set the component properties
void CbsFileSrvDlg::SetProperties(void)
{
c_FileSrv.put_ListeningPort(dlgSettings.m_Port);
if ((dlgSettings.m_edKey) > "")
{
c_FileSrv.put_SecurityMode(2); //shared secret key
c_FileSrv.put_SecretKey(dlgSettings.m_edKey);
}
else
c_FileSrv.put_SecurityMode(0); //no encryption
c_FileSrv.put_UseCompression(dlgSettings.m_chkCompress);
}
//Start the server
void CbsFileSrvDlg::OnBnClickedbtnstart()
{
if (c_FileSrv.Start())
LogMsg("Server started");
else
MessageBox("Cannot start the server!", "Error", MB_OK);
UpdateStatus();
}
停止服务器
为了停止接受连接,我们必须调用方法 _Stop()_。如果我们当前有客户端连接,我们应该使用方法 _RemoveClient()_ 将它们删除。
//Stop the server
void CbsFileSrvDlg::OnBnClickedbtnstop()
{
int i;
LVITEM lvi;
c_FileSrv.Stop();
for (i=c_lvClients.GetItemCount()-1; i>= 0; i--)
{
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
lvi.iItem = i;
lvi.iSubItem = 0;
if (c_lvClients.GetItem(&lvi))
c_FileSrv.RemoveClient(int(lvi.iImage));
c_lvClients.DeleteItem(i);
}
UpdateStatus();
LogMsg("Server stopped");
}
添加新连接
当新客户端连接时,会触发 _OnNewClient_ 事件。此时,我们应该获取客户端信息(地址/端口)并将其句柄存储在某处以供将来使用。
//A new connection is available
void CbsFileSrvDlg::OnNewClientBsfilesrvx(long aHandle)
{
LVITEM lvi;
CString Addr, Port, Tm;
Addr = c_FileSrv.GetClientAddress(aHandle);
Port.Format("%d", c_FileSrv.GetClientPort(aHandle));
int i = c_lvClients.GetItemCount();
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
lvi.iItem = i;
lvi.iSubItem = 0;
lvi.lParam = aHandle;
lvi.pszText = "Not signed in";
c_lvClients.InsertItem(&lvi);
c_lvClients.SetItemText(i, 1, Addr);
c_lvClients.SetItemText(i, 2, Port);
CTime tm = time(NULL);
Tm = tm.Format("%m/%d/%Y %H:%M:%S");
c_lvClients.SetItemText(i, 3, Tm);
c_lvClients.SetItemText(i, 4, "Connected");
UpdateStatus();
LogMsg("New connection from " + Addr + ":" + Port);
}
移除断开连接的客户端
当已连接的客户端断开连接时,会触发 _OnClientDisconnected_ 事件。此时,我们应该释放与该客户端相关的所有数据。
//A connection is broken
void CbsFileSrvDlg::OnClientDisconnectedBsfilesrvx(long aHandle)
{
int i;
LVITEM lvi;
CString Name, Addr, Port;
i = IndexFromHandle(aHandle);
if (i < 0) return;
lvi.mask = LVIF_TEXT;
lvi.iItem = i;
lvi.iSubItem = 0;
c_lvClients.GetItem(&lvi);
Name = c_lvClients.GetItemText(i, 0);
Addr = c_lvClients.GetItemText(i, 1);
Name = c_lvClients.GetItemText(i, 2);
LogMsg("Disconnected " + Name + " " + Addr + ":" + Port);
c_FileSrv.RemoveClient(int(lvi.iImage));
c_lvClients.DeleteItem(i);
UpdateStatus();
}
提供用户密码
建立连接后,客户端会立即发送登录请求。在我们简单的实现中,我们只接受默认用户(_Guest_)。因此,在 _OnNeedPassword_ 事件的处理程序中,所请求的用户名应始终为空,我们始终为密码值传递一个空 _string_。
//A request for a password
void CbsFileSrvDlg::OnNeedPasswordBsfilesrvx(long aHandle,
LPCTSTR aUsername, BOOL* aOkay, BSTR* aPassword)
{
if (aUsername == "")
{
//this is the user Guest
*aOkay = true;
}
else
{
//Unknown user, we accept only guests
*aOkay = false;
}
}
登录
在检查密码后,服务器组件会触发 _OnSignin_ 事件。从那时起,客户端就可以执行各种文件操作了。
//A user is signed-in
void CbsFileSrvDlg::OnSigninBsfilesrvx(long aHandle, long aCode)
{
int i;
CString Name, S;
i = IndexFromHandle(aHandle);
if (i < 0) return;
Name = GetUsername(aHandle);
c_lvClients.SetItemText(i, 0, Name);
S.Format("%s %s:%d", Name, c_FileSrv.GetClientAddress(aHandle),
c_FileSrv.GetClientPort(aHandle));
if (aCode == 0)
S = S + " signed in successfully";
else
S = S + " failed sign-in";
LogMsg(S);
}
列出文件夹内容
客户端登录后执行的第一件事是获取(主)文件夹内容。 _OnNeedListFolder_ 事件的处理程序负责授予或拒绝操作权限。有两个输入参数和两个输出参数:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 所关注文件夹的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to list the folder contents
void CbsFileSrvDlg::OnNeedListFolderBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) + ": list folder " + PreSlash(aPath));
}
列表操作的结束以 _OnListFolderDone_ 事件进行通知。
处理“创建文件夹”请求
当收到客户端的“创建文件夹”请求时,会触发 _OnNeedNewFolder_ 事件。处理程序必须授予或拒绝操作权限。参数是:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 要压缩的文件的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to zip
void CbsFileSrvDlg::OnNeedZipBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) +
": starting zip operaton" + PreSlash(aPath));
}
_OnZipDone_ 事件会告知操作完成情况。
//The zip operation is completed
void CbsFileSrvDlg::OnZipDoneBsfilesrvx(long aHandle, long aCode)
{
LogMsg(GetUsername(aHandle) + ": finishing zip operation");
}
处理“重命名文件”请求
当收到客户端的“重命名文件”请求时,会触发 _OnNeedRename_ 事件。处理程序必须授予或拒绝操作权限。参数是:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 要重命名的文件的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to rename a file
void CbsFileSrvDlg::OnNeedRenameFileBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) + ": rename file " + PreSlash(aPath));
}
处理“删除文件”请求
当收到客户端的“删除文件”请求时,会触发 _OnNeedDeleteFile_ 事件。处理程序必须授予或拒绝操作权限。参数是:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 要删除的文件的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to delete a file
void CbsFileSrvDlg::OnNeedDeleteFileBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) + ": delete file " + PreSlash(aPath));
}
处理下载请求
当收到客户端的下载请求时,会触发 _OnNeedDownload_ 事件。处理程序必须授予或拒绝操作权限。参数是:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 要下载的文件的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to download
void CbsFileSrvDlg::OnNeedDownloadBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) +
": starting download " + PreSlash(aPath));
}
_OnDownloadDone_ 事件会告知操作完成情况。
//The download operation is completed
void CbsFileSrvDlg::OnDownloadDoneBsfilesrvx(long aHandle,
long aCode)
{
LogMsg(GetUsername(aHandle) + ": finishing download");
}
处理上传请求
当收到客户端的上传请求时,会触发 _OnNeedUpload_ 事件。处理程序必须授予或拒绝操作权限。参数是:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 所关注文件的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to upload
void CbsFileSrvDlg::OnNeedUploadBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) + ": starting upload " +
PreSlash(aPath));
}
_OnUploadDone_ 事件会告知操作完成情况。
//The upload operation is completed
void CbsFileSrvDlg::OnUploadDoneBsfilesrvx(long aHandle, long aCode)
{
LogMsg(GetUsername(aHandle) + ": finishing upload");
}
处理 Zipping 请求
当收到客户端的 Zipping 请求时,会触发 _OnNeedZip_ 事件。处理程序必须授予或拒绝操作权限。参数是:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 要压缩的文件的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to zip
void CbsFileSrvDlg::OnNeedZipBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) + ": starting zip operaton" +
PreSlash(aPath));
}
_OnZipDone_ 事件会告知操作完成情况。
//The zip operation is completed
void CbsFileSrvDlg::OnZipDoneBsfilesrvx(long aHandle, long aCode)
{
LogMsg(GetUsername(aHandle) + ": finishing zip operation");
}
处理 Zipping 请求
当收到客户端的 Zipping 请求时,会触发 _OnNeedUnzip_ 事件。处理程序必须授予或拒绝操作权限。参数是:
- _aHandle_ - 触发事件的客户端对象的句柄
- _aPath_ - 要解压缩的文件的相对路径名
- _aOkay_ - 将此变量设置为 _true_ 以允许操作
- _aRoot_ - 如果允许操作,请在此处写入分配给此用户的根文件夹
//A request to unzip
void CbsFileSrvDlg::OnNeedUnzipBsfilesrvx(long aHandle,
LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
CString Dir = GetAppDir();
*aOkay = true;
*aRoot = Dir.AllocSysString();
LogMsg(GetUsername(aHandle) + ": starting unzip operaton" +
PreSlash(aPath));
}
_OnUnzipDone_ 事件会告知操作完成情况。
//The unzip operation is completed
void CbsFileSrvDlg::OnUnzipDoneBsfilesrvx(long aHandle, long aCode)
{
LogMsg(GetUsername(aHandle) + ": finishing unzip operation");
}
客户端
加载示例项目 _FileClient.sln_。可以通过控件变量 _c_FileCln_ 访问客户端组件。让我们回顾一下它的主要属性和方法。
连接到服务器
为了建立与服务器的新连接,我们必须执行以下步骤:
- 如果我们想要加密连接,我们必须将属性 _SecurityMode_ 设置为 _2_,并将 _SecretKey_ 设置为“_Connect_”窗体上指定的加密密钥的值。如果我们不需要加密,我们只需将 _SecurityMode_ 设置为 _0_。
- 如果我们要使用数据包压缩,我们必须将属性 _UseCompression_ 设置为 _TRUE_。
- 调用方法 _Connect(Addr, Port)_,并将服务器的 IP 地址/域名和监听端口作为参数传递。
//Initiate a connection request
void CbsFileClnDlg::OnBnClickedbtnconnect()
{
CString Msg;
if (dlgConnect.DoModal() != IDOK) return;
if (dlgConnect.m_edKey > "")
{
c_FileCln.put_SecurityMode(2); //shared secret key
c_FileCln.put_SecretKey(dlgConnect.m_edKey);
}
else
c_FileCln.put_SecurityMode(0); //no encryption
c_FileCln.put_UseCompression(dlgConnect.m_chkCompress);
if (!c_FileCln.Connect(dlgConnect.m_edHost, dlgConnect.m_edPort))
{
Msg.Format("Error Code: %d", c_FileCln.get_LastError());
AfxMessageBox(Msg);
}
UpdateStatus();
}
与服务器断开连接
为了结束与服务器的连接,我们必须调用方法 _Disconnect()_。
//Disconnect from the server
void CbsFileClnDlg::OnBnClickedbtndisconnect()
{
if (!c_FileCln.get_Connected()) return;
c_FileCln.Disconnect();
OnDisconnectedBsfileclnx();
}
列出文件夹内容
方法 _ListFolder()_ 发送一个请求以获取文件夹内容。
//Request the folder contents
void CbsFileClnDlg::ListFolder(void)
{
CString S;
if (m_NowList) return;
c_stFolder.GetWindowText(S);
if (c_FileCln.ListFolder(S))
{
m_NowList = true;
c_lvFiles.DeleteAllItems();
c_btnList.EnableWindow(false);
if (S > "\\")
OnHaveListItemBsfileclnx("..", true, 0, 0, 0, 0);
}
else
CheckError(c_FileCln.get_LastError());
}
_OnHaveListItem_ 事件为每个可用的文件夹项触发一次。
//A list item is available
void CbsFileClnDlg::OnHaveListItemBsfileclnx(LPCTSTR aName,
BOOL aFolder, long aSizeLo, long aSizeHi, long aTimeLo, long aTimeHi)
{
LVITEM lvi;
CString Sz, Tm;
FILETIME ft;
int i = c_lvFiles.GetItemCount();
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
lvi.iItem = i;
lvi.iSubItem = 0;
if (aFolder)
{
lvi.iImage = 0;
Sz = "";
}
else
{
lvi.iImage = 1;
Sz.Format("%d", aSizeLo);
}
lvi.pszText = (LPTSTR)(LPCTSTR) aName;
c_lvFiles.InsertItem(&lvi);
if (aName == "..")
Tm = "";
else
{
ft.dwLowDateTime = aTimeLo;
ft.dwHighDateTime = aTimeHi;
CTime tm(ft, -1);
Tm = tm.Format("%m/%d/%Y %H:%M:%S");
}
c_lvFiles.SetItemText(i, 1, Sz);
c_lvFiles.SetItemText(i, 2, Tm);
}
列表的结束以 _OnListFolderDone()_ 事件进行通知。
//End of the list
void CbsFileClnDlg::OnListFolderDoneBsfileclnx(long aCode)
{
m_NowList = false;
CheckError (aCode);
UpdateButtons();
}
创建新文件夹
方法 _CreateFolder()_ 发送一个创建新文件夹的请求。
//Request a new folder
void CbsFileClnDlg::OnBnClickedbtnnew()
{
CString Nm, Fol;
if (!GetInputText(Nm, "Create folder", "Folder name:")) return;
c_stFolder.GetWindowText(Fol);
Nm = AddSlash(Fol) + Nm;
if (c_FileCln.CreateFolder(Nm))
m_NowCreateFolder = true;
else
CheckError(c_FileCln.get_LastError());
UpdateButtons();
}
文件夹创建后会触发 _OnCreateFolderDone()_ 事件。
//A new folder is created
void CbsFileClnDlg::OnCreateFolderDoneBsfileclnx(long aCode)
{
m_NowCreateFolder = false;
CheckError(aCode);
UpdateButtons();
if (aCode > 0) return;
ListFolder();
}
重命名文件
方法 _RenameFile()_ 发送一个在服务器上重命名文件的请求。
//Send rename request
void CbsFileClnDlg::OnBnClickedbtnrename()
{
CString Nm, Fol;
if (c_lvFiles.GetSelectedCount() == 0) return;
if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx)
{
//folder
if (!GetInputText(Nm, "Rename folder", "New name:")) return;
Nm = AddSlash(GetCurFol()) + Nm;
if (c_FileCln.RenameFolder(AddSlash(GetCurFol()) +
c_lvFiles.GetItemText(GetSelectedIndex(), 0), Nm))
m_NowRenameFolder = true;
else
CheckError(c_FileCln.get_LastError());
}
else
{
//file
if (!GetInputText(Nm, "Rename file", "New name:")) return;
Nm = AddSlash(GetCurFol()) + Nm;
if (c_FileCln.RenameFile(AddSlash(GetCurFol()) +
c_lvFiles.GetItemText(GetSelectedIndex(), 0), Nm))
m_NowRenameFile = true;
else
CheckError(c_FileCln.get_LastError());
}
UpdateButtons();
}
收到确认后会触发 _OnRenameFileDone()_ 事件。
//A file is renamed
void CbsFileClnDlg::OnRenameFileDoneBsfileclnx(long aCode)
{
m_NowRenameFile = false;
CheckError(aCode);
UpdateButtons();
if (aCode > 0) return;
ListFolder();
}
删除文件
方法 _DeleteFile()_ 发送一个删除文件的请求。
//Send delete request
void CbsFileClnDlg::OnBnClickedbtndelete()
{
CString Nm, Fol;
if (c_lvFiles.GetSelectedCount() == 0) return;
if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx)
{
//folder
if (c_FileCln.DeleteFolder(AddSlash(GetCurFol()) +
c_lvFiles.GetItemText(GetSelectedIndex(), 0)))
m_NowDeleteFolder = true;
else
CheckError(c_FileCln.get_LastError());
}
else
{
//file
if (c_FileCln.DeleteFile(AddSlash(GetCurFol()) +
c_lvFiles.GetItemText(GetSelectedIndex(), 0)))
m_NowDeleteFile = true;
else
CheckError(c_FileCln.get_LastError());
}
UpdateButtons();
}
收到确认后会触发 _OnDeleteFileDone()_ 事件。
//A file is deleted
void CbsFileClnDlg::OnDeleteFileDoneBsfileclnx(long aCode)
{
m_NowDeleteFile = false;
CheckError(aCode);
UpdateButtons();
if (aCode > 0) return;
ListFolder();
}
下载文件
方法 _Download()_ 发送一个下载文件的请求。
//Send a download request
void CbsFileClnDlg::GoDownload(void)
{
CString Nm, Fol;
if (c_lvFiles.GetSelectedCount() == 0) return;
if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx) return;
Nm = c_lvFiles.GetItemText(GetSelectedIndex(), 0);
CFileDialog FileDlg(FALSE, "", Nm, 0, NULL);
if( FileDlg.DoModal() != IDOK ) return;
m_DnldFile = Nm;
if (c_FileCln.Download(AddSlash(GetCurFol()) + Nm,
ExtractFilePath(FileDlg.GetPathName())))
{
m_NowDownload = true;
c_stDownload.SetWindowText(m_DnldFile + ": handshaking");
}
else
CheckError (c_FileCln.get_LastError());
UpdateStatus();
}
_OnDownloadProgress()_ 事件会告知下载进度。
//Download progress info
void CbsFileClnDlg::OnDownloadProgressBsfileclnx(long aCountLo,
long aCountHi, long aSizeLo, long aSizeHi)
{
CString S;
S.Format("%s - %d/%d", m_DnldFile, aCountLo, aSizeLo);
c_stDownload.SetWindowText(S);
}
下载完成后会触发 _OnDownloadDone()_ 事件。
//The download operation is completed
void CbsFileClnDlg::OnDownloadDoneBsfileclnx(long aCode)
{
if (aCode == 0)
c_stDownload.SetWindowText(m_DnldFile + " - Done.");
else
c_stDownload.SetWindowText(m_DnldFile +
" - Aborted: " + ErrorText(aCode));
m_NowDownload = false;
UpdateButtons();
}
上传文件
方法 _Upload()_ 发送一个上传文件的请求。
//Send an upload request
void CbsFileClnDlg::OnBnClickedbtnupload()
{
CFileDialog FileDlg(TRUE, "", NULL, 0, NULL);
if( FileDlg.DoModal() != IDOK ) return;
m_UpldFile = FileDlg.GetFileName();
if (c_FileCln.Upload(FileDlg.GetPathName(), GetCurFol()))
{
m_NowUpload = true;
m_UpldFol = GetCurFol();
c_stUpload.SetWindowText(m_UpldFile + ": handshaking");
}
else
CheckError (c_FileCln.get_LastError());
UpdateStatus();
}
_OnUploadProgress()_ 事件提供上传进度信息。
//Upload progress info
void CbsFileClnDlg::OnUploadProgressBsfileclnx(long aCountLo,
long aCountHi, long aSizeLo, long aSizeHi)
{
CString S;
S.Format("%s - %d/%d", m_UpldFile, aCountLo, aSizeLo);
c_stUpload.SetWindowText(S);
}
上传完成后会触发 _OnUploadDone()_ 事件。
//The upload is completed
void CbsFileClnDlg::OnUploadDoneBsfileclnx(long aCode)
{
if (aCode == 0)
c_stUpload.SetWindowText(m_UpldFile + " - Done.");
else
c_stUpload.SetWindowText(m_UpldFile + " - Aborted: " +
ErrorText(aCode));
m_NowUpload = false;
UpdateButtons();
if (m_UpldFol == GetCurFol())
ListFolder();
}
Zipping
_Zip_ 方法启动一个新的压缩操作。
'Start the zip operation
Private Sub btnZip_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnZip.Click
Dim Nm As String
'ask the user for the zip file name
Nm = InputBox("Zip file name", "Zipping")
If Nm = "" Then Exit Sub 'canceled by the user
If ExtractFileExt(Nm) = "" Then
Nm = Nm + ".zip"
End If
If FileCln.Zip(AddSlash(txtFolder.Text) + Nm, _
AddSlash(txtFolder.Text) + _
lvFiles.SelectedItems(0).Text) Then
'the request is accepted
NowZip = True 'raise the flag
ZipFile = Nm 'remember the zip file
ZipFol = txtFolder.Text 'remember the current folder
txtZip.Text = "Handshaking..."
Else
CheckError(FileCln.LastError) 'the request is rejected
End If
UpdateStatus()
End Sub
我们提示用户输入 zip 文件名,如果文件名不为空,则调用 _Zip_ 方法并传递两个参数。第一个是 zip 归档的相对路径名,第二个是要压缩的文件的相对路径名。如果请求成功,我们会记住当前文件夹和 zip 文件名。否则,我们会显示错误消息。
显示进度信息
_OnZipProgress_ 事件提供 zip 操作的进度信息。
//Progress information is available for the zip operation
void CbsFileClnDlg::OnZipProgressBsfileclnx(long aFileCount,
long aFileTotal, long aByteCountLo, long aByteCountHi,
long aByteTotalLo, long aByteTotalHi)
{
CString S;
//Show the current byte count and the
//total amount of bytes (low 32 bits)
S.Format("%s - %d/%d", m_ZipFile, aByteCountLo, aByteTotalLo);
c_stZip.SetWindowText(S);
}
我们显示两个计数器(仅低 32 位):已压缩的字节数和请求的总字节数。
OnZipDone
_OnZipDone_ 事件报告 zip 操作的结果。
//The zip operation is completed
void CbsFileClnDlg::OnZipDoneBsfileclnx(long aCode)
{
if (aCode == 0)
{
c_stZip.SetWindowText(m_ZipFile + " - Done."); //success
}
else
c_stZip.SetWindowText(m_ZipFile +
" - Aborted: " + ErrorText(aCode)); //there is an error,
// describe it
m_NowZip = false;
if (m_ZipFol == GetCurFol())
ListFolder(); //Refresh the folder
UpdateButtons();
}
我们显示 zip 操作的完成状态,如果当前文件夹受到影响,则更新文件列表。
Unzip
_Unzip_ 方法启动一个新的解压缩操作。
//Start the unzip operation
void CbsFileClnDlg::OnBnClickedbtnunzip()
{
CString Arc, Nm, Fol;
if (c_lvFiles.GetSelectedCount() == 0) return;
if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx) return;
Arc = c_lvFiles.GetItemText(GetSelectedIndex(), 0);
if (ExtractFileExt(Arc) != ".zip") return; //not a zip file
//Ask the user for the extraction folder
if (!GetInputText(Fol, "Unzipping",
"Extract to folder (leave blank for current):")) return;
if (c_FileCln.Unzip(AddSlash(GetCurFol()) + Arc, Fol))
{
//the request is accepted
m_NowUnzip = true; //raise the flag
m_UnzipFile = Arc; //remember the unzip file name
m_UnzipFol = GetCurFol(); //remember the current folder
c_stUnzip.SetWindowText(m_UnzipFile + ": handshaking");
}
else
CheckError (c_FileCln.get_LastError());
UpdateStatus();
}
我们提示用户输入目标文件夹,解压后的文件将放置在该文件夹中。然后,我们调用 _Unzip_ 方法并传递两个参数。第一个是 zip 归档的相对路径名,第二个是目标文件夹的相对路径名。如果请求成功,我们会记住当前文件夹和 zip 文件名。否则,我们会显示错误消息。
OnUnzipProgress
_OnUnzipProgress_ 事件提供 unzip 操作的进度信息。
//Progress information is available for the zip operation
void CbsFileClnDlg::OnUnzipProgressBsfileclnx(long aFileCount,
long aFileTotal, long aByteCountLo, long aByteCountHi,
long aByteTotalLo, long aByteTotalHi)
{
CString S;
//Show the current byte count and the
//total amount of bytes (low 32 bits)
S.Format("%s - %d/%d", m_ZipFile, aByteCountLo, aByteTotalLo);
c_stUnzip.SetWindowText(S);
}
我们显示两个计数器(仅低 32 位):已解压的字节数和归档中的总字节数。
OnUnzipDone
_OnUnzipDone_ 事件报告 unzip 操作的结果。
//The zip operation is completed
void CbsFileClnDlg::OnUnzipDoneBsfileclnx(long aCode)
{
if (aCode == 0)
{
c_stUnzip.SetWindowText(m_UnzipFile + " - Done."); //success
}
else
c_stUnzip.SetWindowText(m_UnzipFile + " - Aborted: " +
ErrorText(aCode)); //there is an error, describe it
m_NowUnzip = false;
if (m_UnzipFol == GetCurFol())
ListFolder(); //Refresh the folder
UpdateButtons();
}
我们显示 unzip 操作的完成状态,如果当前文件夹受到影响,则更新文件列表。
结论
如今,互联网由于各种原因是不安全的。在许多情况下,交换敏感信息可能是一个问题。在本文中,我们介绍了一种简单且经济实惠的安全私有文件共享解决方案,无需证书。
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。