朋友们问我,你的示例代码里只有文件从服务器下载到客户端的介绍,是不是在暗示 Web Service 不能实现文件从客户机上传到服务器?不是,这是我的疏忽,本来应该提到 “文件上传” 的问题!
其实这里所谓 “文件上传” 是很简单的,只不过是 “文件下载” 的逆向思维!客户端把文件读起,并转换成为 Base64 编码的 XML 字符串,作为调用服务器有关接口函数的参数;服务器只要把客户端送过来的XML字符串解码保存成为文件即可!
服务器端代码
FUNCTION SaveFile(sUser as String,sPassWord as String,sFileName as String,sXml as String) as Boolean
IF sUser<>"Admin" OR sPassWord<>"PassWord" then
RETURN .f.
ENDIF
IF STRTOFILE(STRCONV(sXml,14),DataBasePath+sFileName)>0 then
RETURN .t.
ELSE
RETURN .f.
ENDIF
ENDFUNC
Web Service 采用的 Http 协议的 “客户端请求、服务器端响应” 的模式,属于同步机制!也就是说客户端调用服务器接口函数以后,就等待,直到服务器给出结果(或者超时)!这种模式的好处在于:客户机、服务器交互时就像我们在Visual FoxPro 里调用内部函数那样—等待返回结果,这给编程带来了极大地方便!个人感受,用 FTP 控件,交互性没有 Web Service 好!
大家注意一下,我们用 Visual FoxPro 编写 Web Service 时,可以编写很多逻辑代码。换言之,用 Web Service 传送文件时,服务器更客户化,无论是上传、下载文件,我们的服务器都可以完成复杂的客户化的逻辑代码!当然,如果你有兴趣编写 FTP Server 的话,一样可以做到!
说着这么多,归根到底一句话:Web Service 不是为了传送文件而设计的,FTP 天生就是传文件的!可能有人要揍我了,我们辛苦看了2个星期的文章,换回的是一句:Web Service 不是为了传送文件而设计的。
这一点大家一定要注意:Web Service 传文件是此技术的一项副产品,而不是设计时的出发点!Web Service的函数调用模式,特别适合商业逻辑的实现。FTP 是文件传输协议,被广泛使用,自然有它的优势,例如两进制文件直接传递、断点续传等!
所以,我们在平时应用中,如果文件体积不大,并且传送文件的需求集合在 Web Service 应用里的,就应该使用这里我们介绍的方法:用 Web Service 传送文件!如果只是一个孤立文件传送任务或者被传送的文件很大,就应该采用 FTP 或者是 Email 方式!
例如:
DECLARE Integer ZipFiles IN "ziptoolkit.dll" as zip String,String,integer,String
?Zip("F:\zip\abc.bmp;F:\zip\ab.txt","F:\zip\test.zip",9,"")
clear dlls "zip"
解压缩文件
函数声明:DECLARE Integer UnZipFile IN "ziptoolkit.dll" String,String,String
返回值:0>=0成功解压缩文件的数量;-1失败
参数1:被解压缩文件
参数2:释放路径
参数3:解压缩口令。空白字符串表示不设口令。
例如:
DECLARE Integer UnZipFile IN "ziptoolkit.dll" as unzip String,String,String
?unzip("F:\zip\test.zip","F:\zip\t1t\","")
clear dlls "unzip"
为什么这里不用 CursorToXML() 处理 DBF 的传送问题
言归正题,让我们使用 Web Service 传送 DBF 文件。如果您是 BOE 的老读者,也许你会有这样的疑问:DBF 不是可以通过 cursortoxml() 函数直接转为 XML 字符串,为什么要另辟蹊径把 DBF 简单的作为文件、用通用的传送文件的方式处理呢?
PROTECTED FUNCTION ZipEncode64(sFile as string,iLevel as Integer,sPsd as string) as String
LOCAL sTempZipFile,sBase64 as String
sTempZipFile=TempPath+SYS(2015)
DECLARE Integer ZipFiles IN "ziptoolkit.dll" as zip String,String,integer,String
IF zip(sFile,sTempZipFile,iLevel,sPsd)>0
sBase64=this.Encode64(sTempZipFile)
ELSE
sBase64=""
ENDIF
CLEAR DLLS "ZIP"
IF FILE(sTempZipFile)
DELETE FILE &sTempZipFile
ENDIF
RETURN sBase64
ENDFUNC
FUNCTION GetDbf(iZipLevel as Integer,sPsd as string) as String
RETURN this.ZipEncode64(DataBasePath+"employee.dbf;"+DataBasePath+"employee.fpt",iZipLevel,sPsd)
ENDPROC
客户端调用
客户端调用这个Web Service 也是很容易的事情:我们使用 GetDBF() 接口取得承载 Zip 的 XML 字符串,并还原它为test.zip,然后把这个 zip 文件解压缩到临时目录里,就得到了employee 表文件,最后打开并显示 employee!
完整代码如下,具体见GetDBF.prg:
#define TempPath "J:\webs\ClientTemp\"
#define ToolPath "J:\webs\tools\"
LOCAL mywebs as FoxWebService
LOCAL loWS
loWS = NEWOBJECT("Wsclient",HOME()+"ffc\_webservices.vcx")
loWS.cWSName = "FoxWebService"
mywebs = loWS.SetupClient("http://BOEWORKS/FoxWebS/FoxWebService.WSDL", "FoxWebService", "FoxWebServiceSoapPort")
SET PATH TO ToolPath
IF SELECT("employee")>0
USE IN employee
ENDIF
STRTOFILE(STRCONV(mywebs.GetDbf(9,"BOE 数据网络工作室"),14),TempPath+"test.zip",.t.)
DECLARE Integer UnZipFile IN "ziptoolkit.dll" as unZip String,String,String
IF UnZip(TempPath+"test.zip",TempPath,"BOE 数据网络工作室")>0
USE TempPath+"employee"
BROWSE
ENDIF
CLEAR DLLS "unzip"