65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.46/5 (9投票s)

2005 年 11 月 10 日

9分钟阅读

viewsIcon

61597

downloadIcon

2842

如何使用免费 SDK 构建安全的私有文件共享客户端/服务器

引言

如今,大多数加密文件共享解决方案都使用数字证书,这些证书适用于用户基数庞大且事先未知的应用程序。但是,如果您想避免获取和维护证书的成本和麻烦,还有另一种可能的解决方案。在本文中,我们介绍了一个用于安全私有文件共享的简单客户端/服务器解决方案。安全性通过对称加密算法(AES 128)和共享密钥来实现。在已建立小型群组的许多情况下,这可能更有效且更实用。

开始之前

下载并安装 文件共享 SDK 的免费版本,该版本可免费用于非商业用途。客户端/服务器组件采用 128 位 AES 加密,无需任何 SSL 证书。相反,它使用共享密钥,创建一个基于应用程序的虚拟专用网络。除了标准的 FTP 操作(如上传、下载、重命名、删除等)之外,该库还提供了一系列高级功能,例如在服务器上打开可查找流、远程搜索文件和文本、远程 zip 压缩和解压缩。

服务器

加载示例项目 _FileServer.sln_。可以通过控件变量 _c_FileSrv_ 访问服务器组件。让我们回顾一下它的主要属性和方法。

启动服务器

为了开始接受连接,我们必须执行以下步骤:

  1. 将属性 _ListenningPort_ 设置为监听端口号。我们从设置窗体获取该值。在本例中,默认值为 _2345_。
  2. 如果我们要建立加密连接,我们必须将属性 _SecurityMode_ 设置为 _2_,并将 _SecretKey_ 设置为设置窗体上指定的加密密钥的值。如果我们不需要加密,则必须将 _SecurityMode_ 设置为 _0_。
  3. 如果我们要使用数据包压缩,则必须将 _UseCompression_ 设置为 _TRUE_。
  4. 调用方法 _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_ 访问客户端组件。让我们回顾一下它的主要属性和方法。

连接到服务器

为了建立与服务器的新连接,我们必须执行以下步骤:

  1. 如果我们想要加密连接,我们必须将属性 _SecurityMode_ 设置为 _2_,并将 _SecretKey_ 设置为“_Connect_”窗体上指定的加密密钥的值。如果我们不需要加密,我们只需将 _SecurityMode_ 设置为 _0_。
  2. 如果我们要使用数据包压缩,我们必须将属性 _UseCompression_ 设置为 _TRUE_。
  3. 调用方法 _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 操作的完成状态,如果当前文件夹受到影响,则更新文件列表。

结论

如今,互联网由于各种原因是不安全的。在许多情况下,交换敏感信息可能是一个问题。在本文中,我们介绍了一种简单且经济实惠的安全私有文件共享解决方案,无需证书。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.