FTP下载CSV拆分后上传失败:OutputStream空指针异常求助
FTP下载拆分后上传出现OutputStream空指针异常的解决方案
问题背景
我在实现从FTP下载CSV文件、拆分后上传回同一服务器的功能时遇到了问题。具体需求是:下载一个约1000行的CSV,拆分为每个100行的小文件后上传到不同目录。目前已经成功连接FTP、下载并处理了文件,但在保存小文件时出现了OutputStream空指针异常(确认ftpClient和remoteFile都不为空),单独测试上传方法是正常的。
相关代码如下:
下载拆分方法
public boolean getFile1(final String filePath, final String fileName) { String remoteFile = filePath + "/" + fileName; BufferedReader reader = null; try{ InputStream is = ftpClient.retrieveFileStream(remoteFile); reader = new BufferedReader(new InputStreamReader(is)); String line; String file =""; int i = 1; int max = 100; int current = 0; while((line = reader.readLine()) != null){ if(current < max){ file+=line+"\n"; current++; } else{ System.out.println("Else; File: " +file); InputStream stream = new ByteArrayInputStream(file.getBytes(StandardCharsets.UTF_8.name())); putFile(stream, "test", "MOCK_COPY"+i+".csv"); i++; file = ""; current = 0; file+=line+"\n"; } } if(!file.isEmpty()){ System.out.println("End File: " +file); InputStream is2 = IOUtils.toInputStream(file, "UTF-8"); putFile(is2, "test", "MOCK_COPY"+i+".csv"); } } catch (IOException e){ e.printStackTrace(); } finally { try{ reader.close(); } catch (IOException e) { e.printStackTrace(); } } return true; }
上传方法
public Boolean putFile(InputStream inputStream, final String filePath, final String fileName) throws IOException { Long ts = System.currentTimeMillis(); String remoteFile = filePath + "/" + fileName; System.out.println("Start uploading file"); // ftpClient.changeWorkingDirectory("test"); ////this overwrites the existing file // ftpClient.storeFile(remoteFile, inputStream); //inputStream.close(); OutputStream outputStream = ftpClient.storeFileStream(remoteFile); byte[] bytesIn = new byte[4096]; int read = 0; while ((read = inputStream.read(bytesIn)) != -1) { outputStream.write(bytesIn, 0, read); } inputStream.close(); outputStream.close(); System.out.println("The file is uploaded successfully in " + (System.currentTimeMillis() - ts) / 1000 + "s"); return true; }
连接方法
public void connectToServer(String server, String user, String pass, Integer port) throws WebApplicationException { port = port == null ? 21 : null; server = server.replace("ftp://",""); try { ftpClient = new FTPClient(); ftpClient.connect(server, port); ftpClient.login(user, pass); ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); System.out.println("Connected to ftp"); } catch (IOException e) { e.printStackTrace(); throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND) .entity("FTP error: " + e.getMessage()).type(MediaType.TEXT_PLAIN).build()); } }
问题分析
出现空指针的核心原因是你在使用ftpClient.retrieveFileStream()下载文件后,没有调用ftpClient.completePendingCommand()来完成FTP命令。
FTP协议中,retrieveFileStream()是一个异步的获取操作:服务器发送文件数据后,需要客户端明确发送“命令完成”的确认,这个连接通道才能被用于后续的FTP操作(比如上传)。如果不调用completePendingCommand(),你的FTP客户端会一直卡在等待下载命令收尾的状态,此时调用storeFileStream()上传时,服务器不会响应,返回的OutputStream就是null,自然触发空指针异常。
另外你的代码还有两个潜在问题:
connectToServer里的端口赋值逻辑错误:port = port == null ? 21 : null;,如果传入了合法的端口号,会被错误地覆盖为null,导致连接异常(你当前可能是传了null所以没暴露问题)retrieveFileStream()可能返回null(比如远程文件不存在),你的代码没有做判空处理,会直接导致后续的InputStreamReader初始化失败
修复方案
下面是针对问题的具体修复步骤和优化后的代码:
1. 修正下载方法:完成FTP命令+资源优化
在下载完成后必须调用completePendingCommand(),同时用StringBuilder代替String拼接提升性能,增加输入流判空:
public boolean getFile1(final String filePath, final String fileName) { String remoteFile = filePath + "/" + fileName; BufferedReader reader = null; InputStream is = null; try { is = ftpClient.retrieveFileStream(remoteFile); // 判空:避免文件不存在时的空指针 if (is == null) { System.err.println("Failed to download file: " + remoteFile); return false; } reader = new BufferedReader(new InputStreamReader(is)); String line; // 用StringBuilder替代String拼接,减少内存开销 StringBuilder fileContent = new StringBuilder(); int i = 1; final int max = 100; int current = 0; while ((line = reader.readLine()) != null) { if (current < max) { fileContent.append(line).append("\n"); current++; } else { System.out.println("Preparing to upload split file " + i); // 使用try-with-resources自动关闭流 try (InputStream stream = new ByteArrayInputStream(fileContent.toString().getBytes(StandardCharsets.UTF_8))) { putFile(stream, "test", "MOCK_COPY" + i + ".csv"); } i++; fileContent.setLength(0); current = 0; fileContent.append(line).append("\n"); } } // 处理最后一批不足100行的内容 if (fileContent.length() > 0) { System.out.println("Preparing to upload final split file " + i); try (InputStream is2 = IOUtils.toInputStream(fileContent.toString(), StandardCharsets.UTF_8.name())) { putFile(is2, "test", "MOCK_COPY" + i + ".csv"); } } } catch (IOException e) { e.printStackTrace(); return false; } finally { try { // 按顺序关闭资源 if (reader != null) reader.close(); if (is != null) is.close(); // 关键:完成下载命令,释放连接通道 if (ftpClient.isConnected()) { ftpClient.completePendingCommand(); } } catch (IOException e) { e.printStackTrace(); } } return true; }
2. 修正连接方法:修复端口赋值错误
public void connectToServer(String server, String user, String pass, Integer port) throws WebApplicationException { // 正确逻辑:如果port为null用21,否则用传入的port port = port == null ? 21 : port; server = server.replace("ftp://", ""); try { ftpClient = new FTPClient(); ftpClient.connect(server, port); ftpClient.login(user, pass); ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); System.out.println("Connected to ftp"); } catch (IOException e) { e.printStackTrace(); throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND) .entity("FTP error: " + e.getMessage()).type(MediaType.TEXT_PLAIN).build()); } }
3. 修正上传方法:增加OutputStream判空+完成上传命令
public Boolean putFile(InputStream inputStream, final String filePath, final String fileName) throws IOException { Long ts = System.currentTimeMillis(); String remoteFile = filePath + "/" + fileName; System.out.println("Start uploading file: " + remoteFile); OutputStream outputStream = ftpClient.storeFileStream(remoteFile); // 判空:避免上传失败时的空指针 if (outputStream == null) { System.err.println("Failed to get upload stream for: " + remoteFile); inputStream.close(); return false; } byte[] bytesIn = new byte[4096]; int read = 0; while ((read = inputStream.read(bytesIn)) != -1) { outputStream.write(bytesIn, 0, read); } inputStream.close(); outputStream.close(); // 完成上传命令,确认服务器接收成功 if (!ftpClient.completePendingCommand()) { System.err.println("Upload failed for file: " + remoteFile); return false; } System.out.println("The file is uploaded successfully in " + (System.currentTimeMillis() - ts) / 1000 + "s"); return true; }
内容的提问来源于stack exchange,提问作者user1974566
相关产品推荐
相关产品推荐

