【问题标题】:Best way to read Spring Multipartfile line by line in Java 8在 Java 8 中逐行读取 Spring Multipartfile 的最佳方法
【发布时间】:2020-07-28 16:56:18
【问题描述】:

csv Spring Multipartfile 的最佳处理方式是什么? 我以前用过这样的东西:

public void handleFile(MultipartFile multipartFile){
    try{
        InputStream inputStream = multipartFile.getInputStream();
        IOUtils.readLines(inputStream, StandardCharsets.UTF_8)
                .stream()
                .forEach(this::handleLine);
    } catch (IOException e) {
        // handle exception
    }
}

private void handleLine(String s) {
    // do stuff per line
}

据我所知,这首先将整个文件加载到内存中的列表中,然后再对其进行处理,对于具有数十万行的文件,这可能需要相当长的时间。

有没有办法逐行处理它而无需手动实现迭代的开销(即使用read()hasNext(等东西),...)? 我正在为文件系统中的文件寻找类似于此示例的简洁内容:

try (Stream<String> stream = Files.lines(Paths.get("file.csv"))) {
        stream.forEach(this::handleLine);
} catch (IOException e) {
    // handle exception
}

【问题讨论】:

  • 您确定要自己处理每一行,而不是让 CSV 解析库处理 InputStream?
  • 它将满足您的需求吗? baeldung.com/java-read-lines-large-file
  • @VGR 你能推荐一个我可以查看的库吗?
  • 你知道 Java 8 的历史吗? 6 岁 ;)
  • 我发现Apache Commons CSV 相当可靠。

标签: java spring java-8


【解决方案1】:

如果你有 InputStream,你可以使用这个:

InputStream inputStream = multipartFile.getInputStream();
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
                    .lines()
                    .forEach(this::handleLine);

在其他情况下:

无论是多部分文件还是你有多个独立文件,在 Java 8 中都有很多方法可以使用 Stream API:

解决方案 1:

如果您的文件位于不同的目录中,您可以这样做:

假设您有一个 ListString,其中包含您的文件路径,如下所示:

List<String> files = Arrays.asList(
                "/test/test.txt",
                "/test2/test2.txt");

然后你可以读取上述文件的所有行如下:

files.stream().map(Paths::get)
        .flatMap(path -> {
            try {
                return Files.lines(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return Stream.empty();
        }).forEach(System.out::println);

解决方案 2:

您还可以通过以下方式使用Files.walk读取/test/ehsan目录中存在的所有文件行:

try (Stream<Path> stream = Files.walk(Paths.get("/test/ehsan"), 1)) {
    stream.filter(Files::isRegularFile)
            .flatMap(path -> {
                try {
                    return Files.lines(path);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return Stream.empty();
            })
            .forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}

如果你想递归地读取/test/ehsan目录中的所有文件行,你可以这样做:

try (Stream<Path> stream = Files.walk(Paths.get("/test/ehsan"))) {
    stream.filter(Files::isRegularFile)
            .flatMap(path -> {
                try {
                    return Files.lines(path);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return Stream.empty();
            })
            .forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}

如您所见,Files.walk 的第二个参数指定要访问的目录级别的最大数量,如果您不传递它,将使用默认值 Integer.MAX_VALUE

解决方案 3:

不止于此,我们可以走得更远。如果我们想读取存在于两个完全不同的目录(例如/test/ehsan/test2/ehsan1)中的所有文件行怎么办?

我们可以做到但要小心,Stream 不应该太长(因为它降低了我们程序的可读性)最好将它们分解为单独的方法,但是因为不可能编写多个方法这里我会写在一个地方如何做到这一点:

假设您有一个 ListString,其中包含您的目录路径,如下所示

list<String> dirs = Arrays.asList(
                "/test/ehsan",
                "/test2/ehsan1");

那么我们可以这样做:

dirs.stream()
        .map(Paths::get)
        .flatMap(path -> {
            try {
                return Files.walk(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return Stream.empty();
        })
        .filter(Files::isRegularFile)
        .flatMap(path -> { 
            try {
                return Files.lines(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return Stream.empty();
        })
        .forEach(System.out::println);

【讨论】:

  • 您的解决方案都考虑文件系统上的文件。我在谈论 Spring Multipartfile (docs.spring.io/spring/docs/current/javadoc-api/org/…),通常不是。我编辑了我的问题以澄清这一点。
  • @Schlaagi 我刚刚添加了可以在特殊情况下使用的更新
【解决方案2】:
public static List<String> readCSV(String fileName) throws IOException {
        List<String> records = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(
                new FileReader(fileName))) {
            String line;
            while ((line = br.readLine()) != null) {
                records.add(line);
            }
        }
        return records;
}

【讨论】:

  • 这基本上就是IOUtils.readLines的实现。不是我想要的。
猜你喜欢
  • 2019-10-10
  • 2023-03-05
  • 2011-08-13
  • 2013-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-20
相关资源
最近更新 更多