You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Apache Commons Net从FileZilla Server下载PDF至Android设备文件损坏问题排查

FTP文件下载不完整且损坏的问题排查与修复

问题场景

我正尝试使用Apache Commons Net从FileZilla Server向Android设备下载文件。FTP连接、登录、文件存在性验证及下载响应均显示正常,但在Android设备中查看文件时,发现其仅为64KB(与原文件大小不符),且因损坏无法打开。我还尝试下载另一个49KB的文件,结果仅下载了48KB,同样损坏无法打开。我使用Adobe Acrobat Reader和QuickOffice尝试打开文件。

相关代码

String usuarioLogin = params[0];
FTPClient ftp = null;
String LOG_TAG = "FTP";
ftp = new FTPClient();
String server = params[0];
int portNumber = Integer.parseInt(params[1]);
String user = params[2];
String password = params[3];
String filename = params[4];
File localFile = UtilsVictor.crearFichero(params[5]);
ftp.connect(server, portNumber);
Log.d(LOG_TAG, "Connected. Reply: " + ftp.getReplyString());
ftp.enterLocalPassiveMode();
ftp.login(user, password);
Log.d(LOG_TAG, "Logged in");
ftp.setFileType(FTP.LOCAL_FILE_TYPE);
Log.d(LOG_TAG, "Downloading");
OutputStream outputStream = null;
boolean success = false;
outputStream = new BufferedOutputStream(new FileOutputStream( localFile));
success = ftp.retrieveFile(filename, outputStream);
if(success) {
    Log.d(LOG_TAG, "success!!");
} else{
    Log.d(LOG_TAG, "NOOOOT success!!");
}

FileZilla Server日志

(000019)13/01/2018 17:13:14 - (not logged in) (192.168.1.38)> Connected on port 50000, sending welcome message...
(000019)13/01/2018 17:13:14 - (not logged in) (192.168.1.38)> 220-FileZilla Server 0.9.60 beta
(000019)13/01/2018 17:13:14 - (not logged in) (192.168.1.38)> 220-written by Tim Kosse (tim.kosse@filezilla-project.org)
(000019)13/01/2018 17:13:14 - (not logged in) (192.168.1.38)> 220 Please visit https://filezilla-project.org/
(000019)13/01/2018 17:13:14 - (not logged in) (192.168.1.38)> USER VICTOR
(000019)13/01/2018 17:13:14 - (not logged in) (192.168.1.38)> 331 Password required for victor
(000019)13/01/2018 17:13:14 - (not logged in) (192.168.1.38)> PASS ****
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> 230 Logged on
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> TYPE I
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> 200 Type set to I
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> PASV
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> 227 Entering Passive Mode (192,168,1,41,125,49)
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> RETR /Informes/572760344M/aaa.pdf
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> 150 Opening data channel for file download from server of "/Informes/572760344M/aaa.pdf"
(000019)13/01/2018 17:13:14 - victor (192.168.1.38)> 226 Successfully transferred "/Informes/572760344M/aaa.pdf"
(000019)13/01/2018 17:13:48 - victor (192.168.1.38)> disconnected.


问题原因分析

看了你的代码和日志,问题其实很明显:你用了BufferedOutputStream,但没有关闭或刷新这个输出流!缓冲流会把数据暂时存在内存缓冲区里,没达到阈值不会自动写入磁盘。当retrieveFile完成后,缓冲区里剩余的那部分数据还没写进文件,直接结束进程就会丢失,导致文件不完整且损坏。

另外还有个小细节:你设置的文件类型是FTP.LOCAL_FILE_TYPE,这对应ASCII模式,对于PDF这类二进制文件来说应该用FTP.BINARY_FILE_TYPE。虽然日志里服务器返回了TYPE I(二进制模式),可能是FTPClient内部做了修正,但显式设置二进制类型会更稳妥。

修复后的代码

我帮你调整了代码,主要补上了流的关闭和缓冲刷新,同时修正了文件类型:

String usuarioLogin = params[0];
FTPClient ftp = null;
String LOG_TAG = "FTP";
ftp = new FTPClient();
String server = params[0];
int portNumber = Integer.parseInt(params[1]);
String user = params[2];
String password = params[3];
String filename = params[4];
File localFile = UtilsVictor.crearFichero(params[5]);
OutputStream outputStream = null;
try {
    ftp.connect(server, portNumber);
    Log.d(LOG_TAG, "Connected. Reply: " + ftp.getReplyString());
    
    ftp.enterLocalPassiveMode();
    boolean loginSuccess = ftp.login(user, password);
    if (!loginSuccess) {
        Log.e(LOG_TAG, "Login failed: " + ftp.getReplyString());
        return;
    }
    Log.d(LOG_TAG, "Logged in");

    // 显式设置二进制文件类型,适配PDF这类二进制文件
    ftp.setFileType(FTP.BINARY_FILE_TYPE);
    Log.d(LOG_TAG, "Downloading");

    outputStream = new BufferedOutputStream(new FileOutputStream(localFile));
    boolean success = ftp.retrieveFile(filename, outputStream);
    
    // 强制刷新缓冲区,确保所有数据写入磁盘
    outputStream.flush();
    
    if(success) {
        Log.d(LOG_TAG, "success!!");
    } else{
        Log.d(LOG_TAG, "NOOOOT success!!");
        Log.e(LOG_TAG, "Retrieve failed: " + ftp.getReplyString());
    }
} catch (IOException e) {
    Log.e(LOG_TAG, "FTP error: " + e.getMessage(), e);
} finally {
    // 务必关闭输出流,确保缓冲区数据全部写入
    if (outputStream != null) {
        try {
            outputStream.close();
        } catch (IOException e) {
            Log.e(LOG_TAG, "Failed to close stream: " + e.getMessage());
        }
    }
    // 关闭FTP连接,释放资源
    if (ftp != null && ftp.isConnected()) {
        try {
            ftp.logout();
            ftp.disconnect();
        } catch (IOException e) {
            Log.e(LOG_TAG, "Failed to disconnect FTP: " + e.getMessage());
        }
    }
}

关键修复点

  • 增加finally块,确保无论下载成功与否,输出流都会被关闭,缓冲区数据能全部写入文件
  • 调用outputStream.flush()强制把缓冲区数据写入磁盘,避免数据残留
  • 把文件类型改为FTP.BINARY_FILE_TYPE,保证二进制文件传输的正确性
  • 补充异常捕获和错误日志,方便后续排查其他问题

这样修改后,应该就能完整下载文件并正常打开了。


内容的提问来源于stack exchange,提问作者vLopez

火山引擎 最新活动