解决Java文件移动时的NoSuchFileException异常问题
CSV文件移动时出现NoSuchFileException的修复方案
问题背景
使用Java代码遍历指定目录下的CSV文件,根据文件中匹配的值将其移动到对应子目录,但执行时抛出文件不存在异常。
原始代码
@Override public void execute(JobExecutionContext context) { File directoryPath = new File("C:\\csv\\nov"); // Create a new subfolder called "processed" into source directory try { Path processedFolderPath = Path.of(directoryPath.getAbsolutePath() + "/processed"); if (!Files.exists(processedFolderPath) || !Files.isDirectory(processedFolderPath)) { Files.createDirectory(processedFolderPath); } Path invalidFilesFolderPath = Path.of(directoryPath.getAbsolutePath() + "/invalid_files"); if (!Files.exists(invalidFilesFolderPath) || !Files.isDirectory(invalidFilesFolderPath)) { Files.createDirectory(invalidFilesFolderPath); } } catch (IOException e) { throw new RuntimeException(e); } FilenameFilter textFileFilter = (dir, name) -> { String lowercaseName = name.toLowerCase(); if (lowercaseName.endsWith(".csv")) { return true; } else { return false; } }; // List of all the csv files File filesList[] = directoryPath.listFiles(textFileFilter); System.out.println("List of the text files in the specified directory:"); for(File file : filesList) { try { try (var br = new FileReader(file.getAbsolutePath(), StandardCharsets.UTF_16)){ List<CsvLine> beans = new CsvToBeanBuilder(br) .withType(CsvLine.class) .withSeparator('\t') .withSkipLines(3) .build() .parse(); for (CsvLine item : beans) { Path originalPath = file.toPath(); if(item.getValue().equals(2) || item.getValue().equals(43) || item.getValue().equals(32)) { // Move here file into new subdirectory when file is invalid Path copied = Paths.get(file.getParent() + "/invalid_files"); try { // Use resolve method to keep the "processed" as folder br.close(); Files.move(originalPath, copied.resolve(originalPath.getFileName()), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new RuntimeException(e); } }} } catch (Exception e){ e.printStackTrace(); } Path originalPath = file.toPath(); System.out.println(String.format("\nProcessed file : %s, moving the file to subfolder /processed\n", originalPath)); } // Move here file into new subdirectory when file processing is finished Path copied = Paths.get(file.getParent() + "/processed"); try { // Use resolve method to keep the "processed" as folder br.close(); Files.move(originalPath, copied.resolve(originalPath.getFileName()), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new RuntimeException(e); } }
错误信息
java.lang.RuntimeException: java.nio.file.NoSuchFileException: C:\csv\nov\12_21_39.csv at com.wordscore.engine.processor.DataValidationCheckJob.execute(DataValidationCheckJob.java:94) at org.quartz.core.JobRunShell.run(JobRunShell.java:202) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) Caused by: java.nio.file.NoSuchFileException: C:\csv\nov\12_21_39.csv
问题分析
- 重复移动文件:当文件被判定为无效并移动到
invalid_files目录后,后续代码仍尝试将同一个文件移动到processed目录,此时原文件已不存在,抛出异常。 - 代码结构错误:最后一段移动到
processed的代码位于for循环外部,且变量file、br、originalPath不在该作用域内,属于语法错误。 - 流操作冗余:在try-with-resources块内手动调用
br.close(),违背了自动资源管理的设计。 - CSV行遍历逻辑问题:只要有一行匹配就移动文件,但未终止循环,可能导致多次尝试移动同一个文件。
修复后的代码
@Override public void execute(JobExecutionContext context) { File directoryPath = new File("C:\\csv\\nov"); Path processedFolderPath = directoryPath.toPath().resolve("processed"); Path invalidFilesFolderPath = directoryPath.toPath().resolve("invalid_files"); // 创建目标目录 try { if (!Files.exists(processedFolderPath)) { Files.createDirectories(processedFolderPath); } if (!Files.exists(invalidFilesFolderPath)) { Files.createDirectories(invalidFilesFolderPath); } } catch (IOException e) { throw new RuntimeException("创建目录失败", e); } // 筛选CSV文件 FilenameFilter csvFilter = (dir, name) -> name.toLowerCase().endsWith(".csv"); File[] filesList = directoryPath.listFiles(csvFilter); // 处理空目录情况 if (filesList == null || filesList.length == 0) { System.out.println("指定目录下无CSV文件"); return; } System.out.println("待处理的CSV文件列表:"); for (File file : filesList) { boolean isInvalid = false; Path originalPath = file.toPath(); System.out.println("- " + originalPath.getFileName()); try (var br = new FileReader(file.getAbsolutePath(), StandardCharsets.UTF_16)) { List<CsvLine> beans = new CsvToBeanBuilder(br) .withType(CsvLine.class) .withSeparator('\t') .withSkipLines(3) .build() .parse(); // 检查是否存在匹配的行 for (CsvLine item : beans) { Integer value = item.getValue(); if (value != null && (value.equals(2) || value.equals(43) || value.equals(32))) { isInvalid = true; break; // 找到匹配后终止循环,避免重复判断 } } } catch (Exception e) { e.printStackTrace(); isInvalid = true; // 解析失败的文件也视为无效 } // 根据标记移动文件 try { Path targetPath; if (isInvalid) { targetPath = invalidFilesFolderPath.resolve(originalPath.getFileName()); Files.move(originalPath, targetPath, StandardCopyOption.REPLACE_EXISTING); System.out.println("文件已移动到invalid_files: " + originalPath.getFileName()); } else { targetPath = processedFolderPath.resolve(originalPath.getFileName()); Files.move(originalPath, targetPath, StandardCopyOption.REPLACE_EXISTING); System.out.println("文件已移动到processed: " + originalPath.getFileName()); } } catch (IOException e) { throw new RuntimeException("移动文件失败: " + originalPath, e); } } }
修复说明
- 新增状态标记:用
isInvalid变量标记文件是否无效,避免重复移动操作。 - 调整代码结构:将文件移动逻辑整合到for循环内,确保每个文件只被移动一次。
- 优化路径处理:使用
Path.resolve()替代字符串拼接,避免路径格式错误。 - 自动资源管理:移除手动关闭流的操作,依赖try-with-resources自动关闭。
- 异常处理增强:解析失败的文件直接标记为无效,同时完善异常信息。
- 空目录处理:提前判断文件列表是否为空,避免空指针异常。
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

