最近由于公司的规定要把所有的FTP禁止掉,只能用SFTP,所以只能在程序中加入相关支持.由于也要在WINDOWS中加入,折腾了好几天.网络上一般都是一两句话说这个支持,那个可以,可是真的做起来麻烦不少.因此觉得有必要把这个过程写下来供人参考,省得大家走弯路.
前提条件:
1. Visual Studio 2012
2. 静态连接所有程序.动态连接比较麻烦,还要msvcrt.dll之类的一起打包,因此我们一律用静态连接.
首先找到的是libcurl,libcurl的网络说明里什么都支持,包括SFTP,可是后来发现底层要openssl才行,后来又发现光openssl还不行,SFTP是通过libssh2来做的,libssh2的底层其实也是openssl,编译openssl需要用perl来执行一些脚本.所以一共需要perl, openssl, libssh2, libcurl.
第一步,安装perl, 这个是公司电脑支持人员给装的,想来很简单,不说了.
第二步, 编译openssl
1. 下载版本1.0.1m,最新版本是1.0.2,为什么不用呢?因为那个需要一个叫netwide assembler的工具,需要另外下载,否则编译出错, 即使你设置了set OPTS=no-asm. 版本说明里面根本没有写.
2. 在Visual Studio 2012的命令环境中运行
cd ms
set OPTS=no-asm
perl Configure VC-WIN32
perl util\mkfiles.pl >MINFO
perl util\mk1mf.pl %OPTS% VC-WIN32 >32.mak
perl util\mkdef.pl 32 libeay > ms\libeay32.def
perl util\mkdef.pl 32 ssleay > ms\ssleay32.def
nmake -f 32.mak
(参考32all.bat)
第三步, 编译libssh2
1. 下载版本1.5.0
2. 找到libssh2.dsw,双击在visual studio 中打开
3. 选择OpenSSL Lib Release
4. 在项目属性中加入openssl的INCLUDE目录
5. 把/MD 改成 /MT (MD是动态连接,显然是libssh2.dsw的bug)
6. 编译
第四步, 编译libcurl
1. 下载版本7.40.0, 最新版本是7.41.0,但是编译不了!网页上没有7.40.0的连接,只好自己依样画葫芦了.
2. 把openssl和libssh库和头文件copy到curl-7.40.0\..\deps中, 把libssh2.lib 改名成 libssh2_a.lib
3. 在Visual Studio 2012的命令环境中运行
nmake /f Makefile.vc mode=static VC=11 WITH_SSL=static WITH_SSH2=static DEBUG=no
4. 在build出的目录中运行curl.exe -V
curl -V
curl 7.40.0 (i386-pc-win32) libcurl/7.40.0 OpenSSL/1.0.1m WinIDN libssh2/1.5.0
Protocols: dict file ftp ftps gopher http https imap imaps ldap pop3 pop3s rtsp
scp sftp smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile SSPI Kerberos SPNEGO NTLM SSL
完成
第五步,为什么有第五步啊?
本来是不该有的.但是
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
url一定要静态变量!否则就崩溃到curl内部了!
附加1: LIBCURL里用密码方式进行SFTP
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_READFUNCTION, callback_read);
curl_easy_setopt(curl, CURLOPT_READDATA, &sftpFile);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_text);
curl_easy_setopt(curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PASSWORD);
curl_easy_setopt(curl, CURLOPT_USERPWD, (ftpSite->User() + ":" + ftpSite->Pass()).c_str());
curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
res = curl_easy_perform(curl);
附加2: LIBCURL里用公共秘钥方式进行SFTP
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_READFUNCTION, callback_read);
curl_easy_setopt(curl, CURLOPT_READDATA, &sftpFile);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_text);
curl_easy_setopt(curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PUBLICKEY);
curl_easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, publicKey.c_str());
curl_easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, privateKey.c_str());
curl_easy_setopt(curl, CURLOPT_USERNAME, ftpSite->User().c_str());
curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
res = curl_easy_perform(curl);