问题解决:FTPClient在centos7上下载文件之后,文件大小不一致,缺失字符

问题场景

最近有一个采集任务,需要使用spring-boot项目从FTP服务器下载二进制文件到本地,并进行清洗适配。这里使用FTPClient在centos7上下载文件之后,清洗一直报错。后来跟踪到最后,才发现是文件大小不一致,缺失字符,导致清洗时截取字段有问题。

问题环境

软件版本
centos7
jdk8

问题原因

文件是二进制文件,而我们使用FTPClient的代码如下:

ftpClient.enterLocalPassiveMode();
InputStream in = null;
ByteArrayOutputStream output = null;
try {
    in = ftpClient.retrieveFileStream(ftpFilePath);
    output = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024 * 4];
    int len = 0;
    while ((len = in.read(buffer)) != -1) {
        output.write(buffer, 0, len);
    }
} catch (Exception e) {
    throw e;
} finally {
    if (in != null) {
        in.close();
    }
    if (output != null) {
        output.close();
    }
    if (ftp != null) {
        if (!ftp.getClient().completePendingCommand()) {
            log.warn("ftp 关闭连接,对应的ITEM信息如下:{}",itemInfo);
        }
    }
}

这个字符缺失的问题,是因为在传输过程中,做了特定的转换,导致了字符减少。那为什么会发生转换呢?这里就得来说一下FTP传输时候的文件类型设置了。
在这里插入图片描述
一般在跑程序的时候,我们会选择ASCII模式或者BINARY模式,而这两者的区别是对于回车换行的处理。BINARY模式不对数据进行任何处理,ASCII模式将回车换行转换为本机的回车字符,比如:UNIX下是\n,Windows下是\r\nMac下是\r。 比如说,在ASCII模式下会转换文件,这是因为不同的系统有不同的行结束符,如果不做转换,下载下来的文件就会存在显示的问题。UNIX系统下行结束符一个字节,即十六进制的0A,而Windows的系统是两个字节,即十六进制的0D0A,所以当你用ASCII方式从UNIXFTP服务器下载文件到Windows系统上时(不管是什么文件),每检测到一个字节是0A,就会自动插入一个0D。所以很显然,我们这里的原始文件是二进制文件,而FTPClient默认是ASCII模式,将其做了自动替换,导致了最后的文件大小减少。 不过,如果文件本身就是UNIX下的文本文件,使用ASCII模式是正确的,要是使用了BINARY模式,我们在Windows上看这个文件是没有换行的,里面是一个个的黑方块。

解决方案

所以,我们在这里要设置FILE_TYPEBINARY,最后正确的代码如下:

ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.enterLocalPassiveMode();
InputStream in = null;
ByteArrayOutputStream output = null;
try {
    in = ftpClient.retrieveFileStream(ftpFilePath);
    output = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024 * 4];
    int len = 0;
    while ((len = in.read(buffer)) != -1) {
        output.write(buffer, 0, len);
    }
} catch (Exception e) {
    throw e;
} finally {
    if (in != null) {
        in.close();
    }
    if (output != null) {
        output.close();
    }
    if (ftp != null) {
        if (!ftp.getClient().completePendingCommand()) {
            log.warn("ftp 关闭连接,对应的ITEM信息如下:{}",itemInfo);
        }
    }
}

结果

顺利执行下去,并且最后的清洗也没有报错了。
在这里插入图片描述

总结

FTP文件传输还有很多学问可以学习,再接再厉!

随缘求赞

如果我的文章对大家产生了帮忙,可以在文章底部点个赞或者收藏;
如果有好的讨论,可以留言;
如果想继续查看我以后的文章,可以左上角点击关注
拜拜

posted on 2022-11-29 18:40  枫夜求索阁  阅读(209)  评论(0)    收藏  举报

导航