ftpclient.cpp

来自「自己改写的在WINCE上开发用的EVC++的FTP操作示例工程,希望能给相关人士」· C++ 代码 · 共 1,641 行 · 第 1/4 页

CPP
1,641
字号
	while( outputStream.GetNextLine(strLine) )
	{
		std::auto_ptr<CFTPFileStatus> pFtpFileStatus(new CFTPFileStatus());
		if( ftpListParser.Parse(*pFtpFileStatus, strLine) )
		{
			pFtpFileStatus->Path() = strPath;
			vFileList.push_back(pFtpFileStatus.release());
		}
	}
	
	return true;
}

/// Gets the directory listing of the ftp-server. Sends the NLST command to
/// the ftp-server.
/// @param[in] strPath Starting path for the list command.
/// @param[out] vFileList Returns a simple list of the files and folders of the specified directory.
///                       vFileList contains CFTPFileStatus-Objects. Normally these Objects provide 
///                       a lot of information about the file/folder. But the NLST-command provide 
///                       only a simple list of the directory content (no specific information).
/// @param[in] fPasv see documentation of CFTPClient::Passive
bool CFTPClient::NameList(CStringA strPath, TSpFTPFileStatusVector& vFileList, bool fPasv)
{
	COutputStream outputStream(mc_strEolCharacterSequence);
	if( !ExecuteDatachannelCommand(CDatachannelCmd::NLST(), strPath, CRepresentation(CType::ASCII()), fPasv, 0, &outputStream) )
		return false;
	
	vFileList.clear();
	CStringA strLine;
	CFTPListParse ftpListParser;
	outputStream.SetStartPosition();
	while( outputStream.GetNextLine(strLine) )
	{
		std::auto_ptr<CFTPFileStatus> pFtpFileStatus(new CFTPFileStatus());
		pFtpFileStatus->Path() = strPath;
		pFtpFileStatus->Name() = strLine;
		vFileList.push_back(pFtpFileStatus.release());
	}
	
	return true;
}

/// Gets a file from the ftp-server.
/// Uses C functions for file access (very fast).
/// @param[in] strRemoteFile Filename of the sourcefile on the ftp-server.
/// @param[in] strLocalFile Filename of the targetfile on the local computer.
/// @param[in] repType Representation Type (see documentation of CRepresentation)
/// @param[in] fPasv see documentation of CFTPClient::Passive
bool CFTPClient::DownloadFile(CStringA strRemoteFile, CStringA strLocalFile, CRepresentation& repType, bool fPasv)
{
	CFileT file;
	if( !file.Open(strLocalFile, m_fResumeIfPossible ? "ab" : "wb") )
	{
		ReportError(CCnv::ConvertToString(CErrorT::GetErrorDescription()).c_str(), __FILE__, __LINE__);
		return false;
	}
	file.Seek(0, CFileT::orEnd);
	
	bool fRet= false;
	long lRemoteFileSize = 0;
	FileSize(strRemoteFile, lRemoteFileSize);
	
	if(m_pNotification)
		m_pNotification->OnPreReceiveFile(strRemoteFile, strLocalFile, lRemoteFileSize);
	
	fRet = ExecuteDatachannelCommand(CDatachannelCmd::RETR(), strRemoteFile, repType, fPasv, file.Tell(), &file);
	
	if(m_pNotification)
		m_pNotification->OnPostReceiveFile(strRemoteFile, strLocalFile, lRemoteFileSize);
	
	return fRet;
}

/// Puts a file on the ftp-server.
/// Uses C functions for file access (very fast).
/// @param[in] strLocalFile Filename of the the local sourcefile which to put on the ftp-server.
/// @param[in] strRemoteFile Filename of the targetfile on the ftp-server.
/// @param[in] fStoreUnique if true, the ftp command STOU is used for saving
///                         else the ftp command STOR is used.
/// @param[in] repType Representation Type (see documentation of CRepresentation)
/// @param[in] fPasv see documentation of CFTPClient::Passive
bool CFTPClient::UploadFile(CStringA& strLocalFile, CStringA& strRemoteFile, bool fStoreUnique, CRepresentation& repType, bool fPasv)
{
	CFileT file;
	if( !file.Open(strLocalFile, "rb") )
	{
		ReportError(CCnv::ConvertToString(CErrorT::GetErrorDescription()).c_str(), __FILE__, __LINE__);
		return false;
	}
	
	long lRemoteFileSize = 0;
	if( m_fResumeIfPossible )
		FileSize(strRemoteFile, lRemoteFileSize);
	
	CDatachannelCmd cmd(CDatachannelCmd::STOR());
	if( lRemoteFileSize > 0 )
		cmd = CDatachannelCmd::APPE();
	else if( fStoreUnique )
		cmd = CDatachannelCmd::STOU();
	
	file.Seek(0, CFileT::orEnd);
	long lLocalFileSize = file.Tell();
	file.Seek(lRemoteFileSize, CFileT::orBegin);
	
	bool fRet=false;
	
	if(m_pNotification)
		m_pNotification->OnPreSendFile(strLocalFile, strRemoteFile, lLocalFileSize);
	
	fRet = ExecuteDatachannelCommand(cmd, strRemoteFile, repType, fPasv, 0, &file);
	
	if(m_pNotification)
		m_pNotification->OnPostSendFile(strLocalFile, strRemoteFile, lLocalFileSize);
	
	return fRet;
}

/// Executes a commando that result in a communication over the data port.
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] strPath Parameter for the command usually a path.
/// @param[in] representation see documentation of CFTPClient::CRepresentation
/// @param[in] fPasv see documentation of CFTPClient::Passive
/// @param[in] dwByteOffset Server marker at which file transfer is to be restarted.
/// @param[in] pObserver Object for observing the execution of the command.
bool CFTPClient::ExecuteDatachannelCommand(CDatachannelCmd crDatachannelCmd, CStringA& strPath, CRepresentation& representation, 
										   bool fPasv, DWORD dwByteOffset, ITransferNotification* pObserver)
{
	if( m_fTransferInProgress )
		return false;
	
	if( !IsConnected() )
		return false;
	
	// check representation
	if( m_apCurrentRepresentation.get() == NULL )
	{
		CRepresentation *pRep = m_apCurrentRepresentation.release();
		delete pRep;
		
		std::auto_ptr<CRepresentation> rep(new CRepresentation(CType::ASCII())); 
		m_apCurrentRepresentation = rep;
	}
	
	if( representation != *m_apCurrentRepresentation )
	{
		// transmit representation to server
		if( RepresentationType(representation)!=FTP_OK )
			return false;
		*m_apCurrentRepresentation = representation;
	}
	
	std::auto_ptr<IBlockingSocket> apSckDataConnection(m_apSckControlConnection->CreateInstance());
	
	if( fPasv )
	{
		if( !OpenPassiveDataConnection(*apSckDataConnection, crDatachannelCmd, strPath, dwByteOffset) )
			return false;
	}
	else
	{
		if( !OpenActiveDataConnection(*apSckDataConnection, crDatachannelCmd, strPath, dwByteOffset) )
			return false;
	}
	
	bool fTransferOK = TransferData(crDatachannelCmd, pObserver, *apSckDataConnection);
	
	apSckDataConnection->Close();
	
	// get response from ftp server
	CReply Reply;
	if( !fTransferOK || !GetResponse(Reply) || !Reply.Code().IsPositiveCompletionReply() )
		return false;
	
	return true;
}

/// Executes a commando that result in a communication over the data port.
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] pObserver Object for observing the execution of the command.
/// @param[in] sckDataConnection Socket which is used for sending/receiving data.
bool CFTPClient::TransferData(CDatachannelCmd& crDatachannelCmd, ITransferNotification* pObserver, IBlockingSocket& sckDataConnection)
{
	switch( crDatachannelCmd.AsEnum() )
	{
	case CDatachannelCmd::cmdSTOR:
	case CDatachannelCmd::cmdSTOU:
	case CDatachannelCmd::cmdAPPE:
		{
			if( !SendData(pObserver, sckDataConnection) )
				return false;
		}
		break;
	case CDatachannelCmd::cmdRETR:
	case CDatachannelCmd::cmdLIST:
	case CDatachannelCmd::cmdNLST:
		if( !ReceiveData(pObserver, sckDataConnection) )
			return false;
		break;
	default:
		ASSERT( false );
		return false;
	}
	return true;
}

/// Opens an active data connection.
/// @param[out] sckDataConnection
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] strPath Parameter for the command usually a path.
/// @param[in] dwByteOffset Server marker at which file transfer is to be restarted.
bool CFTPClient::OpenActiveDataConnection(IBlockingSocket& sckDataConnection, CDatachannelCmd& crDatachannelCmd, CStringA& strPath, DWORD dwByteOffset)
{
	std::auto_ptr<IBlockingSocket> apSckServer(m_apSckControlConnection->CreateInstance());
	
	USHORT ushLocalSock = 0;
	try
	{
		// INADDR_ANY = ip address of localhost
		// second parameter "0" means that the WINSOCKAPI ask for a port
		CSockAddr csaAddressTemp(INADDR_ANY, 0);
		apSckServer->Create(SOCK_STREAM);
		apSckServer->Bind(csaAddressTemp);
		apSckServer->GetSockAddr(csaAddressTemp);
		ushLocalSock=csaAddressTemp.Port();
		apSckServer->Listen();
	}
	catch(CBlockingSocketException& blockingException)
	{
		ReportError(blockingException.GetErrorMessage(), __FILE__, __LINE__);
		apSckServer->Cleanup();
		return false;
	}
	
	// get own ip address
	CSockAddr csaLocalAddress;
	m_apSckControlConnection->GetSockAddr(csaLocalAddress);
	
	// transmit the socket (ip address + port) to the server
	// the ftp server establishes then the data connection
	if( DataPort(csaLocalAddress.DottedDecimal(), ushLocalSock)!=FTP_OK )
		return false;
	
	// if resuming is activated then set offset
	if( m_fResumeIfPossible && 
		(crDatachannelCmd==CDatachannelCmd::cmdSTOR || crDatachannelCmd==CDatachannelCmd::cmdRETR || crDatachannelCmd==CDatachannelCmd::cmdAPPE ) &&
		(dwByteOffset!=0 && Restart(dwByteOffset)!=FTP_OK) )
		return false;
	
	// send FTP command RETR/STOR/NLST/LIST to the server
	CReply Reply;
	if( !SendCommand(GetCmdString(crDatachannelCmd, strPath), Reply) ||
		!Reply.Code().IsPositivePreliminaryReply() )
		return false;
	
	// accept the data connection
	CSockAddr sockAddrTemp;
	if( !apSckServer->Accept(sckDataConnection, sockAddrTemp) )
		return false;
	
	return true;
}

/// Opens a passive data connection.
/// @param[out] sckDataConnection
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] strPath Parameter for the command usually a path.
/// @param[in] dwByteOffset Server marker at which file transfer is to be restarted.
bool CFTPClient::OpenPassiveDataConnection(IBlockingSocket& sckDataConnection, CDatachannelCmd& crDatachannelCmd, CStringA& strPath, DWORD dwByteOffset)
{
	ULONG   ulRemoteHostIP = 0;
	USHORT  ushServerSock  = 0;
	
	// set passive mode
	// the ftp server opens a port and tell us the socket (ip address + port)
	// this socket is used for opening the data connection 
	if( Passive(ulRemoteHostIP, ushServerSock)!=FTP_OK )
		return false;
	
	// establish connection
	CSockAddr sockAddrTemp;
	try
	{
		sckDataConnection.Create(SOCK_STREAM);
		CSockAddr csaAddress(ulRemoteHostIP, ushServerSock); 
		sckDataConnection.Connect(csaAddress);
	}
	catch(CBlockingSocketException& blockingException)
	{
		ReportError(blockingException.GetErrorMessage(), __FILE__, __LINE__);
		sckDataConnection.Cleanup();
		return false;
	}
	
	// if resuming is activated then set offset
	if( m_fResumeIfPossible && 
		(crDatachannelCmd==CDatachannelCmd::cmdSTOR || crDatachannelCmd==CDatachannelCmd::cmdRETR || crDatachannelCmd==CDatachannelCmd::cmdAPPE ) &&
		(dwByteOffset!=0 && Restart(dwByteOffset)!=FTP_OK) )
		return false;
	
	// send FTP command RETR/STOR/NLST/LIST to the server
	CReply Reply;
	if( !SendCommand(GetCmdString(crDatachannelCmd, strPath), Reply) ||
		!Reply.Code().IsPositivePreliminaryReply() )
		return false;
	
	return true;
}

/// Sends data over a socket to the server.
/// @param[in] pObserver Object for observing the execution of the command.
/// @param[in] sckDataConnection Socket which is used for the send action.
bool CFTPClient::SendData(ITransferNotification* pObserver, IBlockingSocket& sckDataConnection)
{
	try
	{
		m_fTransferInProgress=true;
		
		int iNumWrite;
		size_t bytesRead;
		
		pObserver->OnPreBytesSend(m_vBuffer, bytesRead);
		
		while( !m_fAbortTransfer && bytesRead!=0 )
		{
			iNumWrite = sckDataConnection.Write(m_vBuffer.GetData(), static_cast<int>(bytesRead), mc_uiTimeout);
			ASSERT( iNumWrite == static_cast<int>(bytesRead) );
			
			pObserver->OnPreBytesSend(m_vBuffer, bytesRead);
			
			if(m_pNotification)
				m_pNotification->OnBytesSent(m_vBuffer, iNumWrite);
		}
		
		m_fTransferInProgress=false;
		if( m_fAbortTransfer )
		{
			Abort();
			return false;
		}
		
	}
	catch(CBlockingSocketException& blockingException)
	{
		m_fTransferInProgress=false;
		ReportError(blockingException.GetErrorMessage(), __FILE__, __LINE__);
		sckDataConnection.Cleanup();
		return false;
	}
	
	return true;
}

/// Receives data over a socket from the server.
/// @param[in] pObserver Object for observing the execution of the command.
/// @param[in] sckDataConnection Socket which is used for receiving the data.
bool CFTPClient::ReceiveData(ITransferNotification* pObserver, IBlockingSocket& sckDataConnection)
{
	try
	{
		m_fTransferInProgress = true;
		
		if(m_pNotification)
			m_pNotification->OnBeginReceivingData();
		
		int iNumRead=sckDataConnection.Receive(m_vBuffer.GetData(), m_vBuffer.Length(), mc_uiTimeout);
		long lTotalBytes = iNumRead;
		while( !m_fAbortTransfer && iNumRead!=0 )
		{
			if(m_pNotification)
				m_pNotification->OnBytesReceived(m_vBuffer, iNumRead);
			
			pObserver->OnBytesReceived(m_vBuffer, iNumRead);
			
			iNumRead=sckDataConnection.Receive(m_vBuffer.GetData(), m_vBuffer.Length(), mc_uiTimeout);
			lTotalBytes += iNumRead;
		}
		
		if(m_pNotification)
			m_pNotification->OnEndReceivingData(lTotalBytes);
		
		m_fTransferInProgress=false;
		if( m_fAbortTransfer )
		{
			Abort();
			return false;
		}
	}
	catch(CBlockingSocketException& blockingException)
	{
		m_fTransferInProgress=false;
		ReportError(blockingException.GetErrorMessage(), __FILE__, __LINE__);
		sckDataConnection.Cleanup();
		return false;
	}
	
	return true;
}

/// Returns the command string for a specific command.
/// @param[in] crDatachannelCmd Command for which the string should be returned.
/// @param[in] strPath Parameter which have to be added to the command.
CStringA CFTPClient::GetCmdString(CDatachannelCmd& crDatachannelCmd, CStringA& strPath)
{
	switch( crDatachannelCmd.AsEnum() )
	{
	case CDatachannelCmd::cmdLIST: return strPath.IsEmpty() ? "LIST" : "LIST " + strPath;
	case CDatachannelCmd::cmdNLST: return strPath.IsEmpty() ? "NLST" : "NLST " + strPath;
	case CDatachannelCmd::cmdSTOR: return "STOR " + strPath;
	case CDatachannelCmd::cmdSTOU: return "STOU " + strPath;
	case CDatachannelCmd::cmdRETR: return "RETR " + strPath;
	case CDatachannelCmd::cmdAPPE: return "APPE " + strPath;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?