FtpLib.PRG -- VFP FTP 通信函数库

* FtpLib.PRG -- VFP FTP 通信函数库
* 
* 代码编写: fireghost57
* 维护日期: 2012.10.16
* 
*----------------------------------------------------------------------------* 
*** General WinINET Constants 
#Define INTERNET_DEFAULT_FTP_PORT               21 

#Define INTERNET_OPEN_TYPE_PRECONFIG            0 
#Define INTERNET_OPEN_TYPE_DIRECT               1 
#Define INTERNET_OPEN_TYPE_PROXY                3 

#Define INTERNET_OPTION_CONNECT_TIMEOUT         2 
#Define INTERNET_OPTION_CONNECT_RETRIES         3 
#Define INTERNET_OPTION_DATA_SEND_TIMEOUT       7 
#Define INTERNET_OPTION_DATA_RECEIVE_TIMEOUT    8 
#Define INTERNET_OPTION_LISTEN_TIMEOUT          11 

#Define INTERNET_CONNECTION_MODEM				1
#Define INTERNET_CONNECTION_LAN					2
#Define INTERNET_CONNECTION_PROXY				4
#Define INTERNET_CONNECTION_MODEM_BUSY			8
#Define INTERNET_RAS_INSTALLED					16
#Define INTERNET_CONNECTION_OFFLINE				32
#Define INTERNET_CONNECTION_CONFIGURED			64

#Define INTERNET_SERVICE_FTP                    1 

#Define INTERNET_FLAG_NEED_FILE					16

#Define ERROR_INTERNET_EXTENDED_ERROR           12003 

*** FTP WinInet Service Flags 
#Define INTERNET_FLAG_RELOAD                    2147483648 
#Define INTERNET_FLAG_SECURE                    8388608 
#Define FTP_TRANSFER_TYPE_ASCII                 1 
#Define FTP_TRANSFER_TYPE_BINARY                2 
#Define FTP_TRANSFER_TARGET_EXIST				0			&& do not stop if the target already exists

*** Win32 API Constants 
#Define ERROR_SUCCESS                           0 

*** Access Flags 
#Define GENERIC_READ                            0x80000000 
#Define GENERIC_WRITE                           0x40000000 
#Define GENERIC_EXECUTE                         0x20000000 
#Define GENERIC_ALL                             0x10000000 

*** File Attribute Flags 
#Define FILE_ATTRIBUTE_DIRECTORY      			16
#Define FILE_ATTRIBUTE_HIDDEN                   0x00000002 
#Define FILE_ATTRIBUTE_NORMAL                   128			&& 0x00000080 
#Define FILE_ATTRIBUTE_READONLY                 0x00000001 
#Define FILE_ATTRIBUTE_SYSTEM                   0x00000004 

*** Values for FormatMessage API 
#Define FORMAT_MESSAGE_FROM_SYSTEM              4096 
#Define FORMAT_MESSAGE_FROM_HMODULE             2048 

*** CLASS Struct Define
Define CLASS struct_WIN32_FIND_DATA As Custom
	* 模拟 WIN32_FIND_DATA 结构的类
	Value            = ""
	fileAttributes   = 0
	creationTimeLo   = 0
	creationTimeHi   = 0
	lastAccessTimeHi = 0
	lastAccessTimeLo = 0
	lastWriteTimeHi  = 0
	lastWriteTimeLo  = 0
	fileSizeLo       = 0
	fileName         = ""
	creationTime     = CTOT("")
	lastAccessTime   = CTOT("")
	lastWriteTime    = CTOT("")
	
	PROCEDURE setValue(lcValue)
		* 转换缓冲内容到对象属性
		This.value            = lcValue
		This.fileAttributes   = THIS.buf2num(THIS.value,  0, 4)
		This.creationTimeLo   = THIS.buf2num(THIS.value,  4, 4)
		This.creationTimeHi   = THIS.buf2num(THIS.value,  8, 4)
		This.lastAccessTimeHi = THIS.buf2num(THIS.value, 12, 4)
		This.lastAccessTimeLo = THIS.buf2num(THIS.value, 16, 4)
		This.lastWriteTimeHi  = THIS.buf2num(THIS.value, 20, 4)
		This.lastWriteTimeLo  = THIS.buf2num(THIS.value, 24, 4)
		This.fileSizeLo       = THIS.buf2num(THIS.value, 32, 4)
		This.creationTime     = THIS.ftime2dtime(SUBSTR(THIS.value,  5, 8))
		This.lastAccessTime   = THIS.ftime2dtime(SUBSTR(THIS.value, 13, 8))
		This.lastWriteTime    = THIS.ftime2dtime(SUBSTR(THIS.value, 21, 8))

		This.fileName = ALLTRIM(SUBSTR(THIS.value, 45,250))
		If AT(Chr(0), THIS.fileName) <> 0
			This.fileName = SUBSTR(THIS.fileName, 1, AT(Chr(0), THIS.fileName)-1)
		Endif
	ENDPROC
	
	FUNCTION isDirectory
		Return bitAnd(THIS.fileAttributes,FILE_ATTRIBUTE_DIRECTORY) = FILE_ATTRIBUTE_DIRECTORY
	ENDFUNC
	
	Protected FUNCTION buf2num(lcBuffer, lnOffset, lnBytes)
		* 从缓冲转换 bytes 到一个数值
		lnResult = 0
		For ftp_lnIndex = 1 TO lnBytes
			lnResult = lnResult +;
					   bitlshift(Asc(SUBSTR(lcBuffer, lnOffset + ftp_lnIndex, 1)), (ftp_lnIndex - 1)*8)
		Endfor
		Return  lnResult
	
	Protected FUNCTION ftime2dtime(lcFileTime)
		lcSystemTime = REPLICATE(Chr(0), 16)
		
		Declare INTEGER FileTimeToSystemTime IN kernel32.dll;
			STRING @ lpFileTime,;
			STRING @ lpSystemTime

		FileTimeToSystemTime(@lcFileTime, @lcSystemTime)
		wYear   = THIS.buf2num(lcSystemTime,  0, 2)
		wMonth  = THIS.buf2num(lcSystemTime,  2, 2)
		wDay    = THIS.buf2num(lcSystemTime,  6, 2)
		wHour   = THIS.buf2num(lcSystemTime,  8, 2)
		wMinute = THIS.buf2num(lcSystemTime, 10, 2)
		wSecond = THIS.buf2num(lcSystemTime, 12, 2)
		lcDate = STRTRAN(STR(wMonth,2) + "/" + STR(wDay,2) + "/" + STR(wYear,4), " ","0")
		lcTime = STRTRAN(STR(wHour,2) + ":" + STR(wMinute,2) + ":" + STR(wSecond,2), " ","0")
		lcStoredSet = SET("DATE")
		Set DATE TO MDY
		ltResult = CTOT(lcDate + " " + lcTime)
		Set DATE TO &lcStoredSet
		Return  ltResult
EndDefine

*----------------------------------------------------------------------------* 
PROCEDURE FTP_Init
	PUBLIC ghIPSession, ghFTPSession, gcServer, gcUsername, gcPassword
	PUBLIC gnHTTPPort, gnHTTPConnectType, gnConnectTimeout
	PUBLIC gnError, gcErrorMsg
	PUBLIC glCancelFTP, gnFTPTransferBufferSize
	
	ghIPSession = 0
	ghFTPSession = 0
	gcServer = ""
	gcUsername = ""
	gcPassword = ""
	gnHTTPPort = 21
	gnHTTPConnectType = 1
	gnConnectTimeout = 5
	gnError = 0
	gcErrorMsg = ""
	glCancelFTP = .F.
	gnFTPTransferBufferSize = 1024*1024
	
ENDPROC

*----------------------------------------------------------------------------* 
FUNCTION IsConnected()
	local lnFlags,lcConnectionName
	
	Declare INTEGER InternetGetConnectedStateEx IN wininet.dll;
		INTEGER @ lpdwFlags,;
		STRING	@ lpszConnectionName,;
		INTEGER	  dwNameLen,;
		INTEGER   dwReserved
	
	lnFlags = 0
	lcConnectionName = REPLICATE(CHR(0),250)
	if InternetGetConnectedStateEx(@lnFlags,;
								   @lcConnectionName,;
								   LEN(lcConnectionName),0) == 1
		RETURN .T.
	else
		RETURN .F.
	endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION IsCanConnect(lcRemoteIP)
	local lnResult
	
	toShell = CREATEOBJECT("WScript.Shell")
	lnResult = toShell.Run("ping " + lcRemoteIP, 0, 1)
	If lnResult == 0
		RETURN .T.
	Else
		RETURN .F.
	ENDIF
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_Connect(lcServer, lcUsername, lcPassword)
	local lnFlags,lcConnectionName,lhIP, lhHTTP, lnHTTPPort, lnError, lcErrMsg, lnErrLen
	
	Declare INTEGER InternetOpen IN wininet.dll;
		STRING    sAgent,;
		INTEGER   lAccessType,;
		STRING    sProxyName,;
		STRING    sProxyBypass,;
		INTEGER   dwFlags
	
	Declare INTEGER InternetGetConnectedStateEx IN wininet.dll;
		INTEGER @ lpdwFlags,;
		STRING	@ lpszConnectionName,;
		INTEGER	  dwNameLen,;
		INTEGER   dwReserved
	
	Declare INTEGER InternetConnect IN wininet.dll;
		INTEGER   hInternetSession,;
		STRING    sServerName,;
		INTEGER   nServerPort,;
		STRING    sUsername,;
		STRING    sPassword,;
		INTEGER   lService,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	Declare INTEGER GetLastError IN kernel32.dll
	
	* 检查网络连接
	lnFlags = 0
	lcConnectionName = REPLICATE(CHR(0),250)
	if InternetGetConnectedStateEx(@lnFlags,;
								   @lcConnectionName,;
								   LEN(lcConnectionName),0) == 0
		gcErrorMsg = "Network Connection Not Ready"
		RETURN .F.
	endif
	
	* 检查登录信息
	lcServer   = IIF(!EMPTY(lcServer),lcServer,gcServer)
	lcUsername = TRIM(IIF(!EMPTY(lcUsername),lcUsername,gcUsername))
	lcPassword = TRIM(IIF(!EMPTY(lcPassword),lcPassword,gcPassword))
	
	* 存储登陆信息
	gcServer   = lcServer
	gcUsername = lcUsername
	gcPassword = lcPassword
	
	* 设置默认端口
	if gnHTTPPort == 0
		lnHTTPPort = INTERNET_DEFAULT_FTP_PORT
	else
		lnHTTPPort = gnHTTPPort
	endif
	
	* 重置错误信息
	gnError = 0
	gcErrorMsg = ""
	
	* 打开网络连接
	ghIPSession = InternetOpen("vfp", gnHTTPConnectType, NULL,NULL,0)
	if ghIPSession == 0
		* 返回错误信息
		gnError = GetLastError()
		gcErrorMsg = GetSystemErrorMsg(gnError)
		if gnError == 0
			gcErrorMsg = "Create IP Session Failed"
		endif
		return .F.
	endif
	
	* 登录FTP服务器
	ghFTPSession = InternetConnect(ghIPSession, lcServer, lnHTTPPort, lcUsername, lcPassword, INTERNET_SERVICE_FTP, 0, 0)
	if ghFTPSession == 0
		* 关网络连接
		InternetCloseHandle(ghIPSession)
		
		* 返回错误信息
		gnError = GetLastError()
		gcErrorMsg = GetSystemErrorMsg(gnError)
		if gnError == 0
			gcErrorMsg = "Connect FTP Server Failed"
		endif
		return .F. 
	endif
	return .T.
	
ENDFUNC

*----------------------------------------------------------------------------* 
PROCEDURE FTP_Close
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hIPSession
	
	InternetCloseHandle(ghFTPSession)
	InternetCloseHandle(ghIPSession)
	
	ghFTPSession = 0
	ghIPSession = 0
	
ENDPROC

*----------------------------------------------------------------------------* 
FUNCTION FTP_ErrorMessage()
	
	RETURN gcErrorMsg
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_Command(lcCommand, lnExpectResponse)
	local lhFTPCmd, lnResult, lnBufferSize, lcBuffer, laFileList
	
	Declare INTEGER FtpCommand IN wininet.dll;
		INTEGER   hConnect,;
		INTEGER   fExpectResponse,;
		INTEGER   dwFlags,;
		STRING    lpszCommand,;
		STRING  @ dwContext,;
		INTEGER @ phFtpCommand
	
	Declare INTEGER InternetReadFile IN wininet.dll;
		INTEGER   hFile,;
		STRING    lpBuffer,;
		INTEGER   dwNumberOfBytesToRead,;
		INTEGER @ lpdwNumberOfBytesRead
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	* note that ASCII type is hard-coded
	lnResult = FtpCommand(ghFTPSession, lnExpectResponse,;
						  FTP_TRANSFER_TYPE_ASCII,;
						  lcCommand, 0, @lhFTPCmd)
	
	If lnResult == 0
		Return .F.
	Endif
	
	If lhFTPCmd
		* if there is a return - display it on the screen
		Set MEMOWIDTH TO 100
		lnBufferSize = 128        && reading buffer size
		?
        laFileList = chr(13) + chr(10)
		Do WHILE .T.
			lcBuffer = REPLICATE(Chr(0), lnBufferSize)
			tnBytesRead = 0
			
			If InternetReadFile(lhFTPCmd, @lcBuffer, lnBufferSize, @tnBytesRead) == 1
				lcBuffer = LEFT(lcBuffer, tnBytesRead)
				?? lcBuffer
				laFileList = laFileList + lcBuffer
				If tnBytesRead < lnBufferSize
					Exit
				Endif
			Else
				Exit
			Endif
		Enddo
		InternetCloseHandle(lhFTPCmd)
	Endif
	
	Return .T.
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_OpenFile(lcRemoteFile, lcOpenMode)
	local lnMode,lhFileHandle
	
	Declare INTEGER FtpOpenFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszFileName,;
		INTEGER   dwAccess,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	DO CASE
		* GENERIC_READ
		CASE UPPER(lcOpenMode) == "R"
			lnMode = GENERIC_READ
		
		* GENERIC_WRITE
		CASE UPPER(lcOpenMode) == "W"
			lnMode = GENERIC_WRITE
		
		* GENERIC_EXECUTE
		CASE UPPER(lcOpenMode) == "E"
			lnMode = GENERIC_EXECUTE
		
		* GENERIC_ALL
		CASE UPPER(lcOpenMode) == "A"
			lnMode = GENERIC_ALL
	ENDCASE
	
	lhFileHandle = FtpOpenFile(ghFTPSession, lcRemoteFile,;
							lnMode,;
							INTERNET_FLAG_RELOAD + FTP_TRANSFER_TYPE_BINARY, 0)
	
	return lhFileHandle
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_ReadFile(lhFileHandle,lcReadBuffer,lnReadSize)
	local lnReadBytes
	
	Declare INTEGER InternetReadFile IN wininet.dll;
		INTEGER   hFile,;
		STRING    lpBuffer,;
		INTEGER   dwNumberOfBytesToRead,;
		INTEGER @ lpdwNumberOfBytesRead
	
	lcReadBuffer = SPACE(lnReadSize)
	lnReadBytes = 0
	InternetReadFile(lhFileHandle, @lcReadBuffer, lnReadSize, @lnReadBytes)
	
	return lnReadBytes
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_WriteFile(lhFileHandle,lcWriteBuffer,lnWriteSize)
	local lnWriteBytes
	
	Declare INTEGER InternetWriteFile IN wininet.dll;
		INTEGER   hFile,;
		STRING    lpBuffer,;
		INTEGER   dwNumberOfBytesToWrite,;
		INTEGER @ lpdwNumberOfBytesWritten
	
	lnWriteBytes = 0
	lnWriteSize = IIF(lnWriteSize > LEN(lcWriteBuffer), LEN(lcWriteBuffer), lnWriteSize)
	InternetWriteFile(lhFileHandle, lcWriteBuffer, lnWriteSize, @lnWriteBytes)
	
	return lnWriteBytes
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_CloseFile(lhFileHandle)
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	return InternetCloseHandle(lhFileHandle)
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_GetFileSize(lcRemoteFile)
	local lhFTPFile, lnResult
	
	Declare INTEGER FtpOpenFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszFileName,;
		INTEGER   dwAccess,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	Declare INTEGER FtpGetFileSize IN wininet.dll;
		INTEGER   hFile,;
		INTEGER @ lpdwFileSizeHigh
	
	lhFTPFile = FtpOpenFile(ghFTPSession, lcRemoteFile,;
						 GENERIC_READ,;
						 INTERNET_FLAG_RELOAD + FTP_TRANSFER_TYPE_BINARY, 0)
	if lhFTPFile == 0
		return -1
	endif
	
	lnResult = FtpGetFileSize(lhFTPFile, 0)
	InternetCloseHandle(lhFTPFile)
	
	return lnResult
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_GetFile(lcRemoteFile, lcLocalFile)
	local lhFTPFile, lnRemoteFileSize, lnLocalFileSize, lnResult
	
	Declare INTEGER FtpOpenFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszFileName,;
		INTEGER   dwAccess,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	Declare INTEGER FtpGetFileSize IN wininet.dll;
		INTEGER   hFile,;
		INTEGER @ lpdwFileSizeHigh
	
	Declare INTEGER FtpGetFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszRemoteFile,;
		STRING    lpszNewFile,;
		INTEGER   fFailIfExists,;
		INTEGER   dwFlagsAndAttributes,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	* 读网络文件大小
	lhFTPFile = FtpOpenFile(ghFTPSession, lcRemoteFile,;
						 GENERIC_READ,;
						 INTERNET_FLAG_RELOAD + FTP_TRANSFER_TYPE_BINARY, 0)
	if lhFTPFile == 0
		return .F.
	endif
	lnRemoteFileSize = FtpGetFileSize(lhFTPFile, 0)
	InternetCloseHandle(lhFTPFile)
	
	* 下载网络文件
	lnResult = FtpGetFile(ghFTPSession, lcRemoteFile, lcLocalFile,;
						  FTP_TRANSFER_TARGET_EXIST,;
						  FILE_ATTRIBUTE_NORMAL,;
						  FTP_TRANSFER_TYPE_ASCII, 0)
	if lnResult == 0
		return .F.
	endif
	
	* 读本地文件大小
	if not file(lcLocalFile)
		return .F.
	else
		set compatible on
		lnLocalFileSize = fsize(lcLocalFile)
		set compatible off
	endif
	
	* 对比接收文件大小
	if lnRemoteFileSize == lnLocalFileSize
		return .T.
	else
		return .F.
	endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_PutFile(lcLocalFile, lcRemoteFile)
	local lhFTPFile, lnRemoteFileSize, lnLocalFileSize, lnResult
	
	Declare INTEGER FtpOpenFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszFileName,;
		INTEGER   dwAccess,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	Declare INTEGER FtpGetFileSize IN wininet.dll;
		INTEGER   hFile,;
		INTEGER @ lpdwFileSizeHigh
	
	Declare INTEGER FtpPutFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszLocalFile,;
		STRING    lpszNewRemoteFile,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	* 读本地文件大小
	if not file(lcLocalFile)
		return .F.
	else
		set compatible on
		lnLocalFileSize = fsize(lcLocalFile)
		set compatible off
	endif
	
	* 上传网络文件
	lnResult = FtpPutFile(ghFTPSession, lcLocalFile, lcRemoteFile,;
						  FTP_TRANSFER_TYPE_BINARY, 0)
	if lnResult == 0
		return .F.
	endif
	
	* 读网络文件大小
	lhFTPFile = FtpOpenFile(ghFTPSession, lcRemoteFile,;
						 GENERIC_READ,;
						 INTERNET_FLAG_RELOAD + FTP_TRANSFER_TYPE_BINARY, 0)
	if lhFTPFile == 0
		return .F.
	endif
	lnRemoteFileSize = FtpGetFileSize(lhFTPFile, 0)
	InternetCloseHandle(lhFTPFile)
	
	* 对比接收文件大小
	if lnLocalFileSize == lnRemoteFileSize
		return .T.
	else
		return .F.
	endif
	
ENDFUNC

*-----------------------------------------------------------------------------* 
FUNCTION FTP_RecvFile(lcRemoteFile, lcLocalFile, lnShowTimeout)
	LOCAL lhFTPFile,lhFile,;
		  lnFileSize,lnResult,lnBufferReadCount
	
	Declare INTEGER FtpOpenFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING	  lpszFileName,;
		INTEGER   dwAccess,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	Declare INTEGER FtpGetFileSize IN wininet.dll;
		INTEGER   hFile,;
		INTEGER @ lpdwFileSizeHigh
	
	Declare INTEGER InternetReadFile IN wininet.dll;
		INTEGER   hFile,;
		STRING    lpBuffer,;
		INTEGER   dwNumberOfBytesToRead,;
		INTEGER @ lpdwNumberOfBytesRead
	
	Declare INTEGER GetLastError IN kernel32.dll
	
	gnError = 0
	gcErrorMsg = ""
	
	* 从服务器打开文件
	lhFTPFile = FtpOpenFile(ghFTPSession, lcRemoteFile,;
						 GENERIC_READ,;
						 INTERNET_FLAG_RELOAD + FTP_TRANSFER_TYPE_BINARY, 0)
	IF lhFTPFile == 0
		gnError = GetLastError()
		gcErrorMsg = GetSystemErrorMsg()
		
		RETURN gnError
	ENDIF
	
	* 获取服务器文件大小
	lnFileSize = FtpGetFileSize(lhFTPFile, 0)
	
	* 从磁盘创建文件
	lhFile = FCREATE(lcLocalFile)
	IF lhFile == -1
		gnError = -2
		gcErrorMsg = "Can not create file."
		
		InternetCloseHandle(lhFTPFile)
		
		RETURN gnError
	ENDIF
	
	glCancelFTP = .F.
	tcWriteBuffer = SPACE(gnFTPTransferBufferSize)
	tnFileReadBytes = 0
	lnResult = 0
	lnBufferReadCount = 0
	DO WHILE .T.
		* 从服务器读文件到写缓存
		tnReadBytes = 0
		lnResult = InternetReadFile(lhFTPFile, @tcWriteBuffer, LEN(tcWriteBuffer), @tnReadBytes)
		
		* 将写缓存写入磁盘文件
		IF lnResult == 1 AND tnReadBytes > 0
			IF FWRITE(lhFile,LEFT(tcWriteBuffer, tnReadBytes)) == 0
				gnError = -3
				gcErrorMsg = "Can not write file."
				
				InternetCloseHandle(lhFTPFile)
				FCLOSE(lhFile)
				
				RETURN gnError
			ENDIF
			tnFileReadBytes = tnFileReadBytes + tnReadBytes
			lnBufferReadCount = lnBufferReadCount + 1
			OnFTPTransmitting("Download", lcRemoteFile, tnFileReadBytes, lnFileSize, lnShowTimeout)
		ENDIF
		IF glCancelFTP
			gcErrorMsg = "Download canceled by user"
			gnError = -1
			EXIT
		ENDIF
		IF (lnResult == 1 AND tnReadBytes == 0) OR (lnResult == 0)
			EXIT
		ENDIF
	ENDDO
	IF gnError == 0
		OnFTPTransmitting("Download", lcRemoteFile, lnFileSize, lnFileSize, lnShowTimeout)
	ENDIF
	
	InternetCloseHandle(lhFTPFile)
	FCLOSE(lhFile)
	
	RETURN gnError
	
ENDFUNC

*-----------------------------------------------------------------------------* 
FUNCTION FTP_SendFile(lcLocalFile, lcRemoteFile, lnShowTimeout)
	LOCAL lhFTPFile,lhFile,;
		  lnFileSize,lnResult,lnBufferWriteCount
	
	Declare INTEGER FtpOpenFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING	  lpszFileName,;
		INTEGER   dwAccess,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	Declare INTEGER InternetWriteFile IN wininet.dll;
		INTEGER   hFile,;
		STRING    lpBuffer,;
		INTEGER   dwNumberOfBytesToWrite,;
		INTEGER @ lpdwNumberOfBytesWritten
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	* 从服务器打开文件
	lhFTPFile = FtpOpenFile(ghFTPSession, lcRemoteFile,;
						 GENERIC_WRITE,;
						 INTERNET_FLAG_RELOAD + FTP_TRANSFER_TYPE_BINARY, 0)
	IF lhFTPFile == 0
		gnError = GetLastError()
		gcErrorMsg = GetSystemErrorMsg()
		
		RETURN gnError
	ENDIF
	
	* 从磁盘打开文件
	lhFile = FOPEN(lcLocalFile)
	IF lhFile == -1
		gcErrorMsg = "Source file doesn't exist or is in use..."
		gnError = 1
		
		InternetCloseHandle(lhFTPFile)
		
		RETURN gnError
	ENDIF
	
	* 获取磁盘文件大小
	ADIR(taADIR, lcLocalFile)
	lnFileSize = taADIR[1,2]
	
	gnError = 0
	gcErrorMsg = ""
	tnFileWriteBytes = 0
	lnBufferWriteCount = 0
	DO WHILE .T.
		* 从磁盘读文件到写缓存
		tcWriteBuffer = FRead(lhFile,gnFTPTransferBufferSize)
		IF LEN(tcWriteBuffer) == 0
			OnFTPTransmitting("Upload", lcLocalFile, lnFileSize, lnFileSize, lnShowTimeout)
			EXIT
		ENDIF
		
		* 将写缓存写入服务器文件
		tnWriteBytes = 0
		lnResult = InternetWriteFile(lhFTPFile, tcWriteBuffer, LEN(tcWriteBuffer), @tnWriteBytes)
		IF lnResult == 1 AND tnWriteBytes > 0
			tnFileWriteBytes = tnFileWriteBytes + tnWriteBytes
			lnBufferWriteCount = lnBufferWriteCount + 1
			OnFTPTransmitting("Upload", lcLocalFile, tnFileWriteBytes, lnFileSize, lnShowTimeout)
		ENDIF
		IF glCancelFTP
			gcErrorMsg = "Upload canceled by user"
			gnError = -1
			EXIT
		ENDIF
		IF (lnResult == 1 AND tnWriteBytes == 0) OR (lnResult == 0)
			EXIT
		ENDIF
	ENDDO
	
	InternetCloseHandle(lhFTPFile)
	FCLOSE(lhFile)
	
	RETURN gnError
	
ENDFUNC

*-----------------------------------------------------------------------------* 
FUNCTION OnFTPTransmitting(lcShowText, lcFileName, lnCurrentBytes, lnFinishBytes, lnShowTimeout)
	LOCAL lcProcess,;
		  lnSegment,lnDegree
	
	lnSegment = 25
	lnCurrentBytes = IIF(lnCurrentBytes>lnFinishBytes,lnFinishBytes,lnCurrentBytes)
	lnDegree = (lnCurrentBytes/lnFinishBytes)*lnSegment
	
	* 大于零显示进度和完成提示
	if lnShowTimeout > 0
		if lnDegree < lnSegment
			lcProcess = SPACE(lnSegment)
			for ftp_lnIndex = 1 to lnDegree
				lcProcess = STUFF(lcProcess ,ftp_lnIndex,1,">")
			endfor
			wait window lcShowText + ":" + lcFileName + " [" + lcProcess + "]" nowait
		else
			wait window lcShowText + " Finished" timeout lnShowTimeout
		endif
	endif
	
	* 等于零只显示完成提示
	if lnShowTimeout == 0
		if lnDegree == lnSegment
			wait window lcShowText + " Finished" nowait
		endif
	endif
	
	* 等于 -1 只显示进度
	if lnShowTimeout == -1
		lcProcess = SPACE(lnSegment)
		for ftp_lnIndex = 1 to lnDegree
			lcProcess = STUFF(lcProcess ,ftp_lnIndex,1,">")
		endfor
		wait window lcShowText + ":" + lcFileName + " [" + lcProcess + "]" nowait
	endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_RenameFile(lcRemoteFile, lcRename)
	
	Declare INTEGER FtpRenameFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszExisting,;
		STRING    lpszNew
	
	if FtpRenameFile(ghFTPSession, lcRemoteFile, lcRename) == 1
		return .T.
	else
		return .F.
	endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_DeleteFile(lcRemoteFile)
	
	Declare INTEGER FtpDeleteFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszFileName
	
	if FtpDeleteFile(ghFTPSession, lcRemoteFile) == 1
		return .T.
	else
		return .F.
	endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_CreateDir(lcRemotePath)
	local lcDirectory, lnLen
	
	Declare INTEGER FtpCreateDirectory IN wininet.dll;
		INTEGER   hConnect,;
		STRING  @ lpszCurrentDirectory
	
	Declare INTEGER FtpSetCurrentDirectory IN wininet.dll;
		INTEGER   hConnect,;
		STRING  @ lpszDirectory
	
	Declare INTEGER FtpGetCurrentDirectory IN wininet.dll;
		INTEGER   hConnect,;
		STRING  @ lpszCurrentDirectory,;
		INTEGER @ lpdwCurrentDirectory
	
	tcCreatePath = lcRemotePath
	DO WHILE AT("/",tcCreatePath) <> 0
		FtpCreateDirectory(ghFTPSession, LEFT(tcCreatePath,AT("/",tcCreatePath)-1))
		FtpSetCurrentDirectory(ghFTPSession, LEFT(tcCreatePath,AT("/",tcCreatePath)-1))
		tcCreatePath = SUBSTR(tcCreatePath,AT("/",tcCreatePath)+1)
	ENDDO
	if LEN(tcCreatePath) <> 0
		FtpCreateDirectory(ghFTPSession, tcCreatePath)
		FtpSetCurrentDirectory(ghFTPSession, tcCreatePath)
	endif
	
	lcDirectory = SPACE(256)
	lnLen = Len(lcDirectory)
	If FtpGetCurrentDirectory(ghFTPSession, @lcDirectory, @lnLen) == 1
		Return LEFT(lcDirectory, lnLen)
	Else
		Return ""
	Endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_RemoveDir(lcRemotePath)
	
	Declare INTEGER FtpRemoveDirectory IN wininet.dll;
		INTEGER   hConnect,;
		STRING  @ lpszDirectory
	
	if FtpRemoveDirectory(ghFTPSession, @lcRemotePath) == 1
		return .T.
	else
		return .F.
	endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_GetCurrentDir()
	local lcDirectory, lnLen
	
	Declare INTEGER FtpGetCurrentDirectory IN wininet.dll;
		INTEGER   hConnect,;
		STRING  @ lpszCurrentDirectory,;
		INTEGER @ lpdwCurrentDirectory
	
	lcDirectory = SPACE(256)
	lnLen = Len(lcDirectory)
	If FtpGetCurrentDirectory(ghFTPSession, @lcDirectory, @lnLen) == 1
		Return LEFT(lcDirectory, lnLen)
	Else
		Return ""
	Endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION FTP_SetCurrentDir(lcRemotePath)
	
	Declare INTEGER FtpSetCurrentDirectory IN wininet.dll;
		INTEGER   hConnect,;
		STRING  @ lpszDirectory
	
	if FtpSetCurrentDirectory(ghFTPSession, @lcRemotePath) == 1
		return .T.
	else
		return .F.
	endif
	
ENDFUNC

*----------------------------------------------------------------------------* 
* 需要调用函数,此函数用于清除从路径层到第一层文件夹内所有指定文件,不清除文件夹
FUNCTION FTP_CleanupDir(lcRemotePath, lcSuffixes)
	local lnDirLayer,lnFileCount,lcCurrentPath,laFileArray
	
	* 计算文件夹深度
	lnDirLayer = 0
	tcPathRemain = lcRemotePath
	DO WHILE AT("/",tcPathRemain) <> 0
		lnDirLayer = lnDirLayer + 1
		tcPathRemain = SUBSTR(tcPathRemain,AT("/",tcPathRemain)+1)
	ENDDO
	
	* 设定当前路径
	if FTP_GetCurrentDir() <> lcRemotePath
		if not FTP_SetCurrentDir(lcRemotePath)
			return .F.
		endif
	endif
	
	* 删除当前路径所有文件
	lnFileCount = FTP_GetFileList(lcRemotePath,lcSuffixes,@laFileArray)
	if lnFileCount <> 0
		for ftp_lnIndex = 1 to lnFileCount
			if UPPER(lcSuffixes) == "D"
				FTP_RemoveDir(laFileArray[ftp_lnIndex,1])
			else
				FTP_DeleteFile(laFileArray[ftp_lnIndex,1])
			endif
		endfor
	endif
	
	* 执行深度删除
	lcCurrentPath = lcRemotePath
	DO WHILE lnDirLayer <> 0
		* 路径向上
		lcCurrentPath = LEFT(lcCurrentPath,AT("/",lcCurrentPath,lnDirLayer)-1)
		FTP_SetCurrentDir(lcCurrentPath)
		lnDirLayer = lnDirLayer - 1
		
		* 删除当前路径所有文件
		lnFileCount = FTP_GetFileList(lcCurrentPath,lcSuffixes,@laFileArray)
		if lnFileCount <> 0
			for ftp_lnIndex = 1 to lnFileCount
				if UPPER(lcSuffixes) == "D"
					FTP_RemoveDir(laFileArray[ftp_lnIndex,1])
				else
					FTP_DeleteFile(laFileArray[ftp_lnIndex,1])
				endif
			endfor
		endif
	ENDDO
	
	return FTP_GetCurrentDir()
	
ENDFUNC

*----------------------------------------------------------------------------* 
* 需要调用函数,若 lcSuffixes 为 "D" 则列出目录,否则按照扩展名列出相应文件,存储到数组变量中
FUNCTION FTP_GetFileList(lcRemotePath, lcSuffixes, laFileArray)
	local lcStoredPath, lnPathLen, lnFound, lcFindFileData, lhFind
	
	Declare INTEGER FtpFindFirstFile IN wininet.dll;
		INTEGER   hConnect,;
		STRING    lpszSearchFile,;
		STRING  @ lpFindFileData,;
		INTEGER   dwFlags,;
		INTEGER   dwContext
	
	Declare INTEGER InternetFindNextFile IN wininet.dll;
		INTEGER   hFind,;
		STRING  @ lpvFindData
	
	Declare INTEGER InternetCloseHandle IN wininet.dll;
		INTEGER   hInet
	
	* 储存当前路径并设置目标路径
	lcStoredPath = FTP_GetCurrentDir()
	If lcStoredPath == ""
		Return -1		&& 路径错误
	Endif
	if not FTP_SetCurrentDir(lcRemotePath)
		Return -1		&& 路径错误
	endif
	
	* 创建 API 结构对象
	toFileData = CREATEOBJECT("struct_WIN32_FIND_DATA")
	
	* 找到第一个文件
	lnFound = 0
	lcFindFileData = REPLICATE(Chr(0), 320)
	if UPPER(lcSuffixes) == "D"
		lhFind = FtpFindFirstFile(ghFTPSession, "*.*", @lcFindFileData, INTERNET_FLAG_NEED_FILE, 0)
	else
		lhFind = FtpFindFirstFile(ghFTPSession, lcSuffixes, @lcFindFileData, INTERNET_FLAG_NEED_FILE, 0)
	endif
	
	* 存储目录信息
	If lhFind <> 0
		Do WHILE .T.
			toFileData.setValue(lcFindFileData)
			if UPPER(lcSuffixes) == "D"
				If toFileData.isDirectory()
					lnFound = lnFound + 1
					Dimen laFileArray[lnFound, 3]
					laFileArray[lnFound, 1] = toFileData.fileName
					laFileArray[lnFound, 2] = toFileData.fileSizeLo
					laFileArray[lnFound, 3] = toFileData.lastWriteTime
				Endif
			else
				If Not toFileData.isDirectory()
					lnFound = lnFound + 1
					Dimen laFileArray[lnFound, 3]
					laFileArray[lnFound, 1] = toFileData.fileName
					laFileArray[lnFound, 2] = toFileData.fileSizeLo
					laFileArray[lnFound, 3] = toFileData.lastWriteTime
				Endif
			endif
			* 找下一个文件
			If InternetFindNextFile(lhFind, @lcFindFileData) <> 1
				Exit
			Endif
		Enddo
	Endif
	
	* 释放 API 结构对象
	RELEASE toFileData
	
	* 关闭文件
	InternetCloseHandle(lhFind)
	
	if not FTP_SetCurrentDir(lcStoredPath)
		Return -1		&& 路径错误
	endif
	
	Return  lnFound
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION ByteSize(lnByteNum)
	
	Declare STRING StrFormatByteSize IN shlwapi.dll;
		INTEGER   dw,;
		STRING  @ pszBuf,;
		INTEGER   cchBuf
	
	pszBuf = SPACE(64)
	
	Return StrFormatByteSize(lnByteNum, @pszBuf, Len(pszBuf))
	
ENDFUNC

*----------------------------------------------------------------------------* 
PROCEDURE WinInetSetTimeout(dwTimeoutSecs)
	dwTimeoutSecs = IIF(type("dwTimeoutSecs") = "N", dwTimeoutSecs, gnConnectTimeout)
	
	Declare INTEGER InternetSetOption IN wininet.dll;
		INTEGER   ,;
		INTEGER   ,;
		INTEGER @ ,;
		INTEGER   
	
	dwTimeoutSecs = dwTimeoutSecs * 1000   && to milliseconds
	llRetVal = InternetSetOption(ghIPSession, INTERNET_OPTION_CONNECT_TIMEOUT, @dwTimeoutSecs, 4)
	llRetVal = InternetSetOption(ghIPSession, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, @dwTimeOutSecs, 4)
	llRetVal = InternetSetOption(ghIPSession, INTERNET_OPTION_DATA_SEND_TIMEOUT, @dwTimeOutSecs, 4)
	dwTimeoutSecs = 1  &&// Retry only 1 time
	llRetVal = InternetSetOption(ghIPSession, INTERNET_OPTION_CONNECT_RETRIES, @dwTimeoutSecs, 4)
	
ENDPROC

*-----------------------------------------------------------------------------* 
FUNCTION GetLastInternetError(lnError)
	local lcErrorMsg, lnSize
	
	lnError = IIF(type("lnError")="N",lnError,gnError)
	
	DECLARE INTEGER InternetGetLastResponseInfo IN wininet.dll;
		INTEGER @ lpdwError,;
		STRING  @ lpszBuffer,;
		INTEGER @ lpdwBufferLength
	
	lcErrorMsg = SPACE(1024)
	lnSize = LEN(lcErrorMsg)
	
	InterNetGetLastResponseInfo(@lnError, @lcErrorMsg, @lnSize)
	
	IF lnSize < 2
		RETURN ""
	ENDIF
	
	RETURN SUBSTR(lcErrorMsg,1,lnSize)
	
ENDFUNC

*----------------------------------------------------------------------------* 
FUNCTION GetSystemErrorMsg(lnErrorNo, llAPI)
	LOCAL szMsgBuffer,lnSize
	
	lnErrorNo = IIF(type("lnErrorNo") = "N",lnErrorNo,gnError)
	
	if lnErrorNo == ERROR_INTERNET_EXTENDED_ERROR
		RETURN GetLastInternetError()
	endif
	
	szMsgBuffer = SPACE(512)
	
	Declare INTEGER FormatMessage IN kernel32.dll;
		INTEGER   dwFlags ,;
		INTEGER   lpvSource,;
		INTEGER   dwMsgId,;
		INTEGER   dwLangId,;
		STRING  @ lpBuffer,;
		INTEGER   nSize,;
		INTEGER   Arguments
	
	Declare INTEGER GetModuleHandle IN kernel32.dll;
		STRING    
	
	lnModule = GetModuleHandle("wininet.dll")
	if lnModule <> 0 AND !llAPI
		lnSize = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,lnModule,lnErrorNo,0,@szMsgBuffer,LEN(szMsgBuffer),0)
	ELSE
		lnSize = 0
	endif
	
	if lnSize > 2
		szMsgBuffer = SUBSTR(szMsgBuffer,1,lnSize-2)
	ELSE 
		*** REtry with 12000 less - WinInet return Windows API file error codes 
		lnSize = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,lnErrorNo,0,@szMsgBuffer,LEN(szMsgBuffer),0)
		
		if lnSize > 2
			szMsgBuffer = "Win32 API: " + SUBSTR(szMsgBuffer,1,lnSize-2)
		ELSE
			szMsgBuffer = ""
		endif
	endif
	
	RETURN szMsgBuffer
	
ENDFUNC

*----------------------------------------------------------------------------* 
* End of program. 


 

你可能感兴趣的:(VFP)