问题:
服务器端Java程序嵌入一个Apache ftpserver,客户端拟推广使用FileZilla FTP Client作为上传工具。
程序在 FTP断点续传彻底完成之后对文件进行处理,但是无法确认文件在何时才最终被用户上传完,诸如网络断开、机器死机还是用户手动终止上传等问题造成的FTP上传对于中断服务器端程序来说也没法很好检测到。已确定文件传输使用FTP协议,但是在FTP协议中没有找到对于文件是否彻底上传完毕的命令,在抓包过程中也没有找到对于文件大小的控制符。
解决办法:
1.据搜索得到网友提出的方法是由用户每次断点续传彻底完成之后将已经上传的文件改名,考虑了一下,该方法服务器端程序不易直接控制该过程,需要用户手动处理也不太现实。(NO)
2.修改FileZilla FTP Client客户端源码编译,使每次断点上传过程中向服务器端报告所要上传文件的大小,服务器端则每次用户断点续传完之后检查该已上传文件的大小跟客户端报告的大小比较一下,如果等同则确定该文件已经上传完。(YES)
细节部分:
修改FileZilla FTP Client源码,在每次上传过程前的报告文件名部分将文件名后追加一个点分隔符和文件大小,另外为了客户端能正常地呈现服务器文件的文件名,将每次获取文件列表的文件名经过筛选,如果是以点分隔符和数字结尾的文件名,则将之去掉。
修改服务器端Apache ftpserver嵌入部分,在文件续传结束的监听部分实现对文件名解析得到客户端报告的文件总大小,然后和该文件的具体大小比较,如果等同则确定文件上传完毕,可以调用其它程序处理该文件了。
源码:
src/engine/ftpcontrolsocket.cpp
添加第2802行:
cmd += "." + wxLongLong(pData->localFileSize).ToString();
src/engine/directorylistingparser.cpp
添加第8行:
#include <wx/regex.h>
添加第772行:
wxString exp = _T(".+\\.[0-9]+$");
wxRegEx regex;
regex.Compile(exp);
wxString r_exp = _T("\\.[0-9]+$");
wxRegEx r_regex;
r_regex.Compile(r_exp);
后添加第892行:
if (regex.Matches(entry.name))
{
wxString fileName=entry.name;
m_pControlSocket->LogMessage(MessageType::Status, _("--r file:"+entry.name));
r_regex.ReplaceFirst(&fileName,_T(""));
entry.name=fileName;
}
FTP服务启动类中:
FtpServerFactory serverFactory = new FtpServerFactory();
serverFactory.setUserManager(um);
serverFactory.getFtplets().put("upload", ftplet);
ListenerFactory factory = new ListenerFactory();
factory.setPort(port);
Listener listener=factory.createListener();
serverFactory.addListener("default", listener);
FtpServer server = serverFactory.createServer();
server.start();
ftplet监听器中onUploadEnd方法下:
if(filename.matches(".+\\.\\d+$")){
int lastDotIndex=uploadFile.getName().lastIndexOf(".");
String suffix=uploadFile.getName().substring(lastDotIndex+1);
Long declareFileSize=Long.parseLong(suffix);
Long fileSize=uploadFile.getSize();
if(fileSize<declareFileSize) return;
else{
fileName=fileName.substring(0, lastDotIndex);
uploadFile.move(session.getFileSystemView().getFile(fileName));
}
}
编译:
1)按照https://wiki.filezilla-project.org/Compiling_FileZilla_3_under_Windows说明进行编译,最后使用NSIS对已经编译完的可执行程序打成的安装包在安装过程中会报libgcc_s_dw2-1.dll缺失和libstdc++-6.dll调用错误问题,请到mingw目录下找到这两个同名文件复制/替换过来。
2)java部分(省略)
总结:
1.按照说明编译过程中一定要编译说明来进行编译,比如mingw的版本一定要相同,否则在编译过程中会出现问题。
2.对于文件名的追加文件大小的策略是按照最简单、最小化修改的原则来考虑的,对于上传的文件名不能是以点加数字结尾是比较局限的,不过可以修改该策略,比如点加数字再加.tmp结尾等。
3.对于本文提出的方案如果有其它需求,比如客户端上传对于文件大小、文件格式有要求的也可以借此发挥在客户端开始上传之前验证一下。