Quartz作业读取CSV后移动文件报错:文件被占用如何解决?
问题:Quartz作业处理CSV文件后移动时提示文件被占用
问题描述
我要实现一个Quartz作业,功能是读取指定目录下的.csv文件,处理完成后将其移动到子目录。编写代码后执行时出现文件被占用的异常,即使改用try-with-resources结构后问题仍未解决,请问如何释放文件并完成移动?
初始代码
@Override public void execute(JobExecutionContext context) { File directoryPath = new File("C:\\csv\\nov"); // 在源目录下创建名为"processed"的子文件夹 try { Files.createDirectory(Path.of(directoryPath.getAbsolutePath() + "/processed")); } catch (IOException e) { throw new RuntimeException(e); } FilenameFilter textFileFilter = (dir, name) -> { String lowercaseName = name.toLowerCase(); if (lowercaseName.endsWith(".csv")) { return true; } else { return false; } }; // 获取所有CSV文件列表 File filesList[] = directoryPath.listFiles(textFileFilter); System.out.println("指定目录下的文本文件列表:"); for(File file : filesList) { try { List<CsvLine> beans = new CsvToBeanBuilder(new FileReader(file.getAbsolutePath(), StandardCharsets.UTF_16)) ..... .build() .parse(); for(CsvLine item: beans){ ....... sql查询操作 Optional<ProcessedWords> isFound = processedWordsService.findByKeyword(item.getKeyword()); ...................................... } } catch (Exception e){ e.printStackTrace(); } // 文件处理完成后移动到新子目录 Path copied = Paths.get(file.getAbsolutePath() + "/processed"); Path originalPath = file.toPath(); try { Files.move(originalPath, copied, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new RuntimeException(e); } } }
执行异常信息
org.quartz.SchedulerException: Job threw an unhandled exception. at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.3.2.jar:na] at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) ~[quartz-2.3.2.jar:na] Caused by: java.lang.RuntimeException: java.nio.file.FileSystemException: C:\csv\nov\07_06_26.csv -> C:\csv\nov\07_06_26.csv\processed: The process cannot access the file because it is being used by another process at com.wordscore.engine.processor.ImportCsvFilePostJob.execute(ImportCsvFilePostJob.java:114) ~[main/:na] at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na] ... 1 common frames omitted Caused by: java.nio.file.FileSystemException: C:\csv\nov\07_06_26.csv -> C:\csv\nov\07_06_26.csv\processed: The process cannot access the file because it is being used by another process at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92) ~[na:na] at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103) ~[na:na]
更新后的代码
@Override public void execute(JobExecutionContext context) { File directoryPath = new File("C:\\csv\\nov"); // 在源目录下创建名为"processed"的子文件夹 try { Path path = Path.of(directoryPath.getAbsolutePath() + "/processed"); if (!Files.exists(path) || !Files.isDirectory(path)) { Files.createDirectory(path); } } catch (IOException e) { throw new RuntimeException(e); } FilenameFilter textFileFilter = (dir, name) -> { String lowercaseName = name.toLowerCase(); if (lowercaseName.endsWith(".csv")) { return true; } else { return false; } }; // 获取所有CSV文件列表 File filesList[] = directoryPath.listFiles(textFileFilter); System.out.println("指定目录下的文本文件列表:"); for(File file : filesList) { try { try (var br = new FileReader(file.getAbsolutePath(), StandardCharsets.UTF_16)){ List<CsvLine> beans = new CsvToBeanBuilder(br) ...... .build() .parse(); for (CsvLine item : beans) { ..... if (isFound.isPresent()) { ......... }} } catch (Exception e){ e.printStackTrace(); } // 文件处理完成后移动到新子目录 Path copied = Paths.get(file.getAbsolutePath() + "/processed"); Path originalPath = file.toPath(); try { Files.move(originalPath, copied, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new RuntimeException(e); } } }
解决方案
1. 修正文件移动的目标路径错误
你当前的移动路径逻辑完全错误:
Path copied = Paths.get(file.getAbsolutePath() + "/processed");
这段代码会生成类似C:\csv\nov\07_06_26.csv\processed的路径,相当于要把文件移动到以原文件名命名的目录下的processed子目录,这不符合需求,同时也会导致系统尝试创建嵌套路径时出错。
正确的目标路径应该是原目录下的processed子目录,保留原文件名:
// 目标路径:原目录/processed/原文件名 Path targetPath = Paths.get(directoryPath.getAbsolutePath(), "processed", file.getName());
2. 确保CSV解析器完全释放文件资源
虽然你用了try-with-resources包裹FileReader,但建议改用NIO的Files.newBufferedReader,它的资源管理更可靠,同时要确保CsvToBean解析完成后没有持有流的引用:
try (var br = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_16)) { List<CsvLine> beans = new CsvToBeanBuilder(br) .withType(CsvLine.class) // 替换为你的实体类 // 可添加其他配置,比如分隔符、跳过表头等 .build() .parse(); // 处理CSV数据的逻辑 } catch (Exception e) { e.printStackTrace(); }
3. 检查数据库操作是否持有文件引用
处理CSV行时的数据库操作(比如processedWordsService.findByKeyword)如果不小心传入了文件相关的流或对象,可能导致资源未释放。确保数据库操作只处理数据,不持有任何文件资源。
4. 完整修正后的代码示例
@Override public void execute(JobExecutionContext context) { File directoryPath = new File("C:\\csv\\nov"); Path processedDir = Path.of(directoryPath.getAbsolutePath(), "processed"); // 创建processed目录(仅当不存在时) try { if (!Files.exists(processedDir)) { Files.createDirectory(processedDir); } } catch (IOException e) { throw new RuntimeException("创建processed目录失败", e); } // 过滤CSV文件 FilenameFilter csvFilter = (dir, name) -> name.toLowerCase().endsWith(".csv"); File[] filesList = directoryPath.listFiles(csvFilter); if (filesList == null) { System.out.println("指定目录下无CSV文件或目录不存在"); return; } System.out.println("待处理的CSV文件列表:"); for (File file : filesList) { System.out.println(file.getName()); // 处理CSV文件 try (var br = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_16)) { List<CsvLine> beans = new CsvToBeanBuilder(br) .withType(CsvLine.class) // 添加你的配置,比如.withSeparator(',')等 .build() .parse(); for (CsvLine item : beans) { // 执行数据库操作逻辑 Optional<ProcessedWords> isFound = processedWordsService.findByKeyword(item.getKeyword()); // ... 其他业务处理 } } catch (Exception e) { e.printStackTrace(); // 处理失败时可选择跳过移动,或标记为错误文件 continue; } // 移动文件到processed目录 Path originalPath = file.toPath(); Path targetPath = processedDir.resolve(file.getName()); try { Files.move(originalPath, targetPath, StandardCopyOption.REPLACE_EXISTING); System.out.println("已移动文件: " + file.getName()); } catch (IOException e) { throw new RuntimeException("移动文件失败: " + file.getName(), e); } } }
5. 额外排查点
- 检查是否有其他进程(如文本编辑器、杀毒软件)正在访问这些CSV文件
- Windows系统中,确保文件流完全关闭后再执行移动操作(try-with-resources会自动关闭,但需确认无隐式引用)
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

