# servlet 从 WAR 内读取 CSV 文件

> 原文： [http://zetcode.com/articles/warcsv/](http://zetcode.com/articles/warcsv/)

在本教程中，我们从`WEB-INF`目录中的 CSV 文件读取数据。 我们使用 servlet，JSP 文件和 JSTL 库。 Web 应用已部署在 Jetty 上。 OpenCSV 库用于读取 CSV 数据。

## CSV

CSV（逗号分隔值）格式是在电子表格和数据库中使用的非常流行的导入和导出格式。

在以下 Web 应用中，我们从 WAR 文件中的 CSV 文件读取数据，并将数据显示在网页中。 标记人口超过一亿的国家。

```java
pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           ├───bean
│   │           │       Country.java
│   │           ├───service
│   │           │       CountryService.java
│   │           └───web
│   │                   ReadCountries.java
│   ├───resources
│   │       countries.csv
│   └───webapp
│           index.jsp
│           listCountries.jsp
│           showError.jsp
└───test
    └───java

```

这是项目结构。

`pom.xml`

```java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zetcode</groupId>
    <artifactId>readcsvfromwar</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>12</maven.compiler.source>
        <maven.compiler.target>12</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.opencsv</groupId>
            <artifactId>opencsv</artifactId>
            <version>4.6</version>
        </dependency>

        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.2</version>
            </plugin>

            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.14.v20181114</version>
            </plugin>
        </plugins>
    </build>

</project>

```

该项目使用以下依赖项：`avax.servlet-api`，`opencsv`和`jstl`。

`resources/countries.csv`

```java
Name, Population
Slovakia,5429000
Norway,5271000
Croatia,4225000
Russia,143439000
Mexico,122273000
Vietnam,95261000
Sweden,9967000
Iceland,337600
Israel,8622000
Hungary,9830000
Germany,82175700
Japan,126650000

```

这是`countries.csv`文件。 它位于`src/main/resources`目录中。 生成应用后，文件将复制到 WAR 的`WEB-INF/classes`目录。

`com/zetcode/bean/Country.java`

```java
package com.zetcode.bean;

import com.opencsv.bean.CsvBindByName;

import java.util.Objects;

public class Country {

    @CsvBindByName
    private String name;

    @CsvBindByName
    private int population;

    public Country() {
    }

    public Country(String name, int population) {

        this.name = name;
        this.population = population;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Country country = (Country) o;
        return population == country.population &&
                Objects.equals(name, country.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, population);
    }
}

```

这是一个`Country` bean，具有两个属性：`name`和`population`。

```java
@CsvBindByName
private String name;

```

`@CsvBindByName`将`name`属性映射到`Name`列中的字段。

`com/zetcode/CountryService.java`

```java
package com.zetcode.service;

import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
import com.zetcode.bean.Country;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;

public class CountryService {

    public static Optional<List<Country>> getListOfCountries() throws IOException {

        List<Country> countries;

        try (InputStream is = CountryService.class.getClassLoader()
                .getResourceAsStream("countries.csv")) {

            if (is == null) {

                return Optional.empty();
            }

            HeaderColumnNameMappingStrategy<Country> strategy
                    = new HeaderColumnNameMappingStrategy<>();
            strategy.setType(Country.class);

            try (var br = new BufferedReader(
                    new InputStreamReader(is, StandardCharsets.UTF_8))) {

                CsvToBean<Country> csvToBean = new CsvToBeanBuilder<Country>(br)
                        .withType(Country.class)
                        .withMappingStrategy(strategy)
                        .withIgnoreLeadingWhiteSpace(true)
                        .build();

                 countries = csvToBean.parse();
            }
        }

        return Optional.of(countries);
    }
}

```

`CountryService`从 CSV 文件读取数据。

```java
try (InputStream is = CountryService.class.getClassLoader()
    .getResourceAsStream("countries.csv")) {

```

我们使用`getResourceAsStream()`方法将`InputStream`转换为`countries.csv`文件。

```java
if (is == null) {

    return Optional.empty();
}

```

如果未打开输入流，则返回空`Optional`。 这用于避免`null`值。

```java
HeaderColumnNameMappingStrategy<Country> strategy
    = new HeaderColumnNameMappingStrategy<>();
strategy.setType(Country.class);

```

我们使用 OpenCSV 的`HeaderColumnNameMappingStrategy`将`Country` bean 映射到 CSV 文件中的行。 每行都转换为一个 bean。 映射是在`@CsvBindByName`注解的帮助下完成的。

```java
try (var br = new BufferedReader(
        new InputStreamReader(is, StandardCharsets.UTF_8))) {

    CsvToBean<Country> csvToBean = new CsvToBeanBuilder<Country>(br)
            .withType(Country.class)
            .withMappingStrategy(strategy)
            .withIgnoreLeadingWhiteSpace(true)
            .build();

     countries = csvToBean.parse();
}

```

使用`CsvToBeanBuilder`，我们解析 CSV 文件并将行转换为`Country` bean 列表。

`com/zetcode/web/ReadCountries.java`

```java
package com.zetcode.web;

import com.zetcode.bean.Country;
import com.zetcode.service.CountryService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Optional;

@WebServlet(name = "ReadCountries", urlPatterns = {"/read"})
public class ReadCountries extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=UTF-8");

        Optional<List<Country>> countries = CountryService.getListOfCountries();

        String templateName;

        if (countries.isPresent()) {

            request.setAttribute("countries", countries.get());
            templateName = "listCountries.jsp";
        } else {

            templateName = "showError.jsp";
        }

        var dispatcher = request.getRequestDispatcher(templateName);
        dispatcher.forward(request, response);
    }
}

```

在`ReadCountries` Servlet 中，我们称为`getListOfCountries()`服务方法。 如果有一些国家，我们将返回的国家列表设置为`request`对象作为属性。 处理被传送到`listCountries.jsp`。 如果找不到数据，则返回错误消息。

`webapp/listCountries.jsp`

```java
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Countries</title>
    <style>
        .marked { color: chocolate }
    </style>
</head>
<body>
<table>

    <thead>
        <tr>
            <th>Country</th>
            <th>Population</th>
        </tr>
    </thead>

    <tbody>

        <c:forEach items="${countries}" var="count">

            <c:if test="${count.population > 100000000}">
                <tr class="marked">
                    <td>
                        <c:out value="${count.name}"/>
                    </td>
                    <td>
                        <fmt:formatNumber type="number" value="${count.population}" />
                    </td>                   
                </tr>
            </c:if>
            <c:if test="${count.population < 100000000}">
                <tr>
                    <td>
                        <c:out value="${count.name}"/>
                    </td>
                    <td>
                        <fmt:formatNumber type="number" value="${count.population}" />
                    </td>                   
                </tr>
            </c:if>
        </c:forEach>

    </tbody>
    </table>
</body>
</html>

```

在`listCountries.jsp`文件中，我们在 HTML 表中显示数据。

```java
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

```

我们使用两个 JSTL 标签库：核心库和格式库。

```java
<c:forEach items="${countries}" var="count">

```

使用`<c:forEach>`标签，我们遍历`countries`对象。

```java
<c:if test="${count.population > 100000000}">
    <tr class="marked">
        <td>
            <c:out value="${count.name}"/>
        </td>
        <td>
            <fmt:formatNumber type="number" value="${count.population}" />
        </td>                   
    </tr>
</c:if>

```

如果该国家/地区的人口超过一亿，则使用`marked`类作为行；否则，使用`marked`类作为行。 它以另一种颜色显示行。 该测试使用 JSTL 的`<c:if>`标签执行。 `<fmt:formatNumber>`标签用于格式化该值。

`webapp/index.jsp`

```java
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>List countries</title>
    </head>
    <body>
        <a href="read">List countries</a>
    </body>
</html>

```

`index.jsp`包含一个调用`ReadCountries` servlet 的链接。 Servlet 从 CSV 文件读取数据，然后将数据以视图的形式返回给浏览器。

`webapp/showError.jsp`

```java
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Error</title>
    </head>
    <body>
        <p>No countries found</p>
    </body>
</html>

```

此模板文件显示错误消息。

在本教程中，我们展示了如何读取 WAR 文件中的 CSV 数据。

您可能也对以下相关教程感兴趣： [Java 教程](/lang/java/)， [Jersey 应用中的 Web URL](/articles/url/) ， [Java 验证教程](/java/validationfilter/)和 [OpenCSV 教程](/articles/opencsv/)。

列出[所有 Java servlet 教程](/all/#servlets)。