在 Java 8 中逐行读取 Spring Multipartfile 的最佳方法

Sch*_*agi 2 java spring java-8

什么是最好的处理 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
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Tas*_*isi 5

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

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

在其他情况下:

无论是多部分文件还是您有多个独立文件,在 Java 8 中使用StreamAPI有很多方法可以做到:

解决方案1:

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

想象一下,你有一个ListString包含了像下面的文件路径:

List<String> files = Arrays.asList(
                "/test/test.txt",
                "/test2/test2.txt");
Run Code Online (Sandbox Code Playgroud)

然后您可以阅读上述文件的所有行,如下所示:

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

解决方案2:

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

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();
}
Run Code Online (Sandbox Code Playgroud)

如果你想递归地读取/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();
}
Run Code Online (Sandbox Code Playgroud)

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

解决方案3:

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

可以做但是要慎重,Stream不要太长(因为会降低我们程序的可读性)最好把它们分成不同的方法,但是因为这里不能写多个方法,所以我会写进去一个地方如何做到这一点:

想象一下,你有一个ListString包含您的目录,如下面的路径

list<String> dirs = Arrays.asList(
                "/test/ehsan",
                "/test2/ehsan1");
Run Code Online (Sandbox Code Playgroud)

然后我们可以这样做:

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);
Run Code Online (Sandbox Code Playgroud)