klvoek

klvoek

CCTray FTP中文文件上传失败

    这次使用CCTray自动编译发布器,在发布一个项目时总是失败,后来查看CCTray的发布日志和FTP服务器日志才发现原来是中文文件上传乱码。

Filezilla Upload Error

    图中红线标出的地方,则是对应我在上传中文名文件时候的FTP服务器记录,原文件名为“中国.log”,而服务器获得的命令是“/???.log”,继而返回给客户端550 can't access file的错误。(至于为何有这个can't access file,windows下面?号貌似有特殊意义哈)。

    接下来就是解决办法:

 

    第一个被尝试的解决办法:技术人员让步,既然中文文件不能上传,那我们就不用中文名。在CCTray的proj文件中全部使用英文名好了。好在当时仅仅是特例才触发的上面这个问题,也就是从问题出现的时候开始注意就可以了。

    一段时间以后,我又再次为一个vs解决方案编写CCTray proj脚本,然后非常牙疼的发现有一个项目的生成文件是中文名,而这个生成文件是被做到了安装文件中安装到客户端了,在之前的测试过程中已经在很多台电脑上安装了多次,名字最好不要改动否则就是大麻烦。怎么办?当然是动动CCTray的心思了~~

 

    第二种方法:简单修改edtFTPnet源代码,重新编译edtFTPnet动态库    

         (官网论坛中记载,将Encoding设置为Encoding.GetEncoding('GB2312')也可以解决与FTP服务器通信中文内容问题,测试正确)

    想他CCTray一个比较有名气的项目自动编译工具竟然不支持中文文件上传,说来有点可笑的。经过观察,CCTray能够在界面上显示中文,所以不能上传中文的问题就初步定位在FTP上传的这个功能模块上。后来在网上尝试Google了一下答案,中文的&英文的,没有专门针对这一点的。倒是从林林总总的有关FTP上传文件chucuo的文章上获得了一个有价值的信号,FTP上传文件乱码必定和FTP服务器与客户端两者使用的通信流编码不同有关系。当然了,如果一方使用中文,而一方只能使用英文,乱码是必然的。而FileZilla服务器和其他客户端通信已然正常。所以怀疑是CCTray的FTP模块使用的编码不同。根据一般性原则,FileZilla使用的是UTF-8而CCTray就不知道是其他什么编码了。曾经想试试能不能更改FileZilla服务器的字符编码集,但是看了看,没有对应设置选项。倒是客户端有哈哈。

    于是,接下来的主要工作就是翻翻CCTray的FTP功能模块。先下载CCTray的源代码,vs2005不能打开,vs2010安全转换打开~  

 CCTray

    CCTray里面竟然就这么一点点东西,太惊讶。

    Core FtpLib

    在core项目的util下面找到了FtpLib类,看了会儿源代码发现CCTray使用的是一个叫做entFTPnet.dll的东东。找了一下在CCTray没有对应的项目,是使用的第三方dll。然后用Reflector反编译这个动态库,

Reflector

简单看了一下代码,没有发现明显设置FTP连接的编码为非UTF-8,当然也咩有明显的将它设置为UTF-8的代码。后来想,看代码的方式太牛逼了,能力不济。还是使用  猜测-验证  的方法。于是去找entFTPnet到底是个什么东西。到底他是个什么东西文章后面有它的相关链接,感兴趣的可以去看看。居然entFTPnet有源代码有免费,下载一份到本地。依旧vs2005打不开,换vs2010转换打开。在新解决方案中添加了一个测试项目(控制台程序),写了下代码发现封装真是很好。

KlvoeK Test For edtFTPnet

    console是我添加的项目,注意下面表红线的AssemblyInfo.cs后面需要更改它里面的版本号。下图是我写的测试代码(共十行)

KlvoeK Test For edtFTPnet

 

    编译运行,如果你有FTP服务器就会见到CCTray和触发的一样的中文文件上传问题。

 

    之前我在想,这个edtFTPnet提供的FTPConnection应该有设置通信流编码的属性。看过代码发现如下

    FTPConnection有CommandEncoding(用来设置发送FTP命令时的通信流编码)和DataEncoding(用来设置发送数据时的通信流编码)属性,而这两个属性是分别影射到了FTPClient的属性ContorlEncoding和DataEncoding。也就是要设置的是FTPClient的属性。而这两个属性默认是不赋值的(为null)。上面FTP上传文件因为中文乱码出错时发生在发送FTP命令(STOR /中国.log => STOR /???.log),而类FTPControlSocket是控制发送FTP命令的。第一步尝试将两个属性的默认值更改为System.Text.Encoding.UTF8.编译调试,在登录的第一步发送USER fish就产生错误(Unrecognised command)。调试跟踪到类FTPControlSocket的一个函数

        internal virtual void WriteCommand(string command)
        {            
            Log(DEBUG_ARROW + command, true);
            
            // send it
            writer.Write(command + EOL);
        }
看着(StreamWriter)write的Write方法,我就联想到了之前绝望的经历。于是产生了如下的写法。别问我为什么有这种写法。只有不同处是,writer.Write按照StreamWriter的Encoding编码写入string 而 writer.BaseStream.Wrte 可以写入byte[]。

        internal virtual void WriteCommand(string command)
        {            
            Log(DEBUG_ARROW + command, true);
            
            // send it
            //writer.Write(command + EOL);
            this.encoding = (this.encoding == null ? Encoding.UTF8 : this.encoding);
            byte[] datas = this.encoding.GetBytes(command+EOL);
            writer.BaseStream.Write(datas,0,datas.Length);
            writer.BaseStream.Flush();
        }
两种方法在使用的encoding都一样时应该是一样效果才对。但是,后面的方法会产生正确的效果(传递正确的FTP命令),而前者传递给FTP服务器的命令有多余内容导致命令无法识别的错误。具体不同会是什么呢?

 

    到这里,找到了解决中文传递乱码问题。然后就是要将新的dll替换掉原有的dll。但是,大家都知道dll替换有风险,行事需谨慎。下图是要替换的三个dll

Dll Replace

第一次替换,触发CCTray编译:

Dll not found

需要把自己生成dll设置成1.3.0.0版本,于是修改AssemblyInfo.cs 文件(这个文件参考之前的图,在项目的ftp/net目录下)。再次生成替换。OK了,问题解决算是告一段落。

 

CCTray源代码下载(官网自己找):http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET;jsessionid=D525602476765F137898380208561B61

edtFTPnet源代码下载(官网自己找):http://www.enterprisedt.com/products/edtftpnet/overview.html

.Net Reflector(免费版):

测试代码下载(友情提供,可以看看):http://www.itivy.com/DownloadFile.ashx?id=634461734213484399

 

Posted by klvoek @ 2011-7-13 16:54:30 阅读(979) 评论(0)
上一篇:MSBuild不能编译vs安装项目
下一篇:官方文档并不是拿来唬人的

我也来参与讨论

你还可以输入600/600个字符 发表评论
称呼: (必填) 登录 | 开通博客
邮箱: (选填) 你的邮箱地址不会被公开
网站: (选填)
验证码: (必填)
看不清换一张 看不清楚换一张