您的位置:首页 > 运维架构 > Apache

Java开发FTP功能的apache工具包,小心使用为妙

2014-05-22 22:08 417 查看
项目是一个报表系统,使用apache-commons-net网络工具包实现文件上传与下载。实际测试中报表数量比较小,没有发现大问题,并且开发时也是本地开发,本地FTP服务器处理。后来测试发现,该处理使用的是单字节不刷新读取模式,一个文件如果几百M,读入缓冲字节流的数据也会在内存中占用几百个M,这一点实际开发测试可以断点调试,使用VisualVM监控即可。

查看FTP上传下载源码,发现可以再FTPClient注入缓冲字节大小,实际测试8*1024性能比较好:

ftpClient.changeWorkingDirectory(new String(path.getBytes("utf-8"), "ISO8859-1"));
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
// long start = System.currentTimeMillis();
ftpClient.setBufferSize(1024 * 8);
ftpClient.storeFile(new String(fileName.getBytes("utf-8"), "ISO8859-1"), input);
// System.out.println(System.currentTimeMillis()-start);


设置后,发现效率并没有显著提高,查看storeFile文件上传源码了,里面的核心处理还记基于流的读写,如下所示:

protected boolean _storeFile(String command,
String remote,
InputStream local) throws IOException {
Socket socket = _openDataConnection_(command, remote);

if (socket == null) {
return false;
}

OutputStream output = getBufferedOutputStream(socket.getOutputStream());

if (__fileType == ASCII_FILE_TYPE) {
output = new ToNetASCIIOutputStream(output);
}

CSL csl = null;
if (__controlKeepAliveTimeout > 0) {
csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout);
}

// Treat everything else as binary for now
try {
Util.copyStream(local, output, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE,
__mergeListeners(csl), false);
} catch (IOException e) {
Util.closeQuietly(socket); // ignore close errors here
if (csl != null) {
csl.cleanUp(); // fetch any outstanding keepalive replies
}
throw e;
}

output.close(); // ensure the file is fully written
socket.close(); // done writing the file
if (csl != null) {
csl.cleanUp(); // fetch any outstanding keepalive replies
}
// Get the transfer response
boolean ok = completePendingCommand();
return ok;
}


其中的
Util.copyStream
是主要的处理方法,这里的最后一个参数为false,表示是否实时flush,但是这个方法的属性设置 包中没有外部接口,坑爹了。进去看一看这个上传的源码:
public static final long copyStream(InputStream source, OutputStream dest,
int bufferSize, long streamSize,
CopyStreamListener listener,
boolean flush)
throws CopyStreamException
{
int bytes;
long total = 0;
<strong>byte[] buffer = new byte[bufferSize >= 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE];</strong>

try
{
while ((bytes = source.read(buffer)) != -1)
{
// Technically, some read(byte[]) methods may return 0 and we cannot
// accept that as an indication of EOF.

if (bytes == 0)
{
bytes = source.read();
if (bytes < 0) {
break;
}
dest.write(bytes);
<strong>if(flush) {
dest.flush();
}</strong>
++total;
if (listener != null) {
listener.bytesTransferred(total, 1, streamSize);
}
continue;
}

dest.write(buffer, 0, bytes);
<strong>if(flush) {
dest.flush();
}</strong>
total += bytes;
if (listener != null) {
listener.bytesTransferred(total, bytes, streamSize);
}
}
}
catch (IOException e)
{
throw new CopyStreamException("IOException caught while copying.",
total, e);
}

return total;
}


看看加粗的代码,一个是缓冲流大小,一个是是否刷新流。找了半天flush没有注入接口,没办法,最后想通过继承的方式,在应用中实现这个类,并重写方法,最后发现,难度有点大,依赖属性太多。最好把FTPClient直接拉到自己应用中,全部复制吧,如下:

public class<strong> FTPClient extends org.apache.commons.net.ftp.FTPClient</strong> {

/**
* The system property ({@value} ) which can be used to override the system type.<br/>
* If defined, the value will be used to create any automatically created parsers.
*
* @since 3.0
*/
public static final String FTP_SYSTEM_TYPE = "org.apache.commons.net.ftp.systemType";

/**
* The system property ({@value} ) which can be used as the default system type.<br/>
* If defined, the value will be used if the SYST command fails.
*
其它代码照搬吧。不管怎么样,最后的传输效率大大提高,而且是时刻刷新,减轻了服务器压力。

可能对此还不是深入理解,遗漏出望指教。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐