原文: http://zetcode.com/articles/warcsv/

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

CSV

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

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

  1. pom.xml
  2. src
  3. ├───main
  4. ├───java
  5. └───com
  6. └───zetcode
  7. ├───bean
  8. Country.java
  9. ├───service
  10. CountryService.java
  11. └───web
  12. ReadCountries.java
  13. ├───resources
  14. countries.csv
  15. └───webapp
  16. index.jsp
  17. listCountries.jsp
  18. showError.jsp
  19. └───test
  20. └───java

这是项目结构。

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  5. http://maven.apache.org/xsd/maven-4.0.0.xsd">
  6. <modelVersion>4.0.0</modelVersion>
  7. <groupId>com.zetcode</groupId>
  8. <artifactId>readcsvfromwar</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <packaging>war</packaging>
  11. <properties>
  12. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  13. <maven.compiler.source>12</maven.compiler.source>
  14. <maven.compiler.target>12</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <dependency>
  18. <groupId>javax.servlet</groupId>
  19. <artifactId>javax.servlet-api</artifactId>
  20. <version>4.0.1</version>
  21. <scope>provided</scope>
  22. </dependency>
  23. <dependency>
  24. <groupId>com.opencsv</groupId>
  25. <artifactId>opencsv</artifactId>
  26. <version>4.6</version>
  27. </dependency>
  28. <dependency>
  29. <groupId>jstl</groupId>
  30. <artifactId>jstl</artifactId>
  31. <version>1.2</version>
  32. </dependency>
  33. </dependencies>
  34. <build>
  35. <plugins>
  36. <plugin>
  37. <groupId>org.apache.maven.plugins</groupId>
  38. <artifactId>maven-war-plugin</artifactId>
  39. <version>3.2.2</version>
  40. </plugin>
  41. <plugin>
  42. <groupId>org.eclipse.jetty</groupId>
  43. <artifactId>jetty-maven-plugin</artifactId>
  44. <version>9.4.14.v20181114</version>
  45. </plugin>
  46. </plugins>
  47. </build>
  48. </project>

该项目使用以下依赖项:avax.servlet-apiopencsvjstl

resources/countries.csv

  1. Name, Population
  2. Slovakia,5429000
  3. Norway,5271000
  4. Croatia,4225000
  5. Russia,143439000
  6. Mexico,122273000
  7. Vietnam,95261000
  8. Sweden,9967000
  9. Iceland,337600
  10. Israel,8622000
  11. Hungary,9830000
  12. Germany,82175700
  13. Japan,126650000

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

com/zetcode/bean/Country.java

  1. package com.zetcode.bean;
  2. import com.opencsv.bean.CsvBindByName;
  3. import java.util.Objects;
  4. public class Country {
  5. @CsvBindByName
  6. private String name;
  7. @CsvBindByName
  8. private int population;
  9. public Country() {
  10. }
  11. public Country(String name, int population) {
  12. this.name = name;
  13. this.population = population;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. public int getPopulation() {
  22. return population;
  23. }
  24. public void setPopulation(int population) {
  25. this.population = population;
  26. }
  27. @Override
  28. public boolean equals(Object o) {
  29. if (this == o) return true;
  30. if (o == null || getClass() != o.getClass()) return false;
  31. Country country = (Country) o;
  32. return population == country.population &&
  33. Objects.equals(name, country.name);
  34. }
  35. @Override
  36. public int hashCode() {
  37. return Objects.hash(name, population);
  38. }
  39. }

这是一个Country bean,具有两个属性:namepopulation

  1. @CsvBindByName
  2. private String name;

@CsvBindByNamename属性映射到Name列中的字段。

com/zetcode/CountryService.java

  1. package com.zetcode.service;
  2. import com.opencsv.bean.CsvToBean;
  3. import com.opencsv.bean.CsvToBeanBuilder;
  4. import com.opencsv.bean.HeaderColumnNameMappingStrategy;
  5. import com.zetcode.bean.Country;
  6. import java.io.BufferedReader;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.io.InputStreamReader;
  10. import java.nio.charset.StandardCharsets;
  11. import java.util.List;
  12. import java.util.Optional;
  13. public class CountryService {
  14. public static Optional<List<Country>> getListOfCountries() throws IOException {
  15. List<Country> countries;
  16. try (InputStream is = CountryService.class.getClassLoader()
  17. .getResourceAsStream("countries.csv")) {
  18. if (is == null) {
  19. return Optional.empty();
  20. }
  21. HeaderColumnNameMappingStrategy<Country> strategy
  22. = new HeaderColumnNameMappingStrategy<>();
  23. strategy.setType(Country.class);
  24. try (var br = new BufferedReader(
  25. new InputStreamReader(is, StandardCharsets.UTF_8))) {
  26. CsvToBean<Country> csvToBean = new CsvToBeanBuilder<Country>(br)
  27. .withType(Country.class)
  28. .withMappingStrategy(strategy)
  29. .withIgnoreLeadingWhiteSpace(true)
  30. .build();
  31. countries = csvToBean.parse();
  32. }
  33. }
  34. return Optional.of(countries);
  35. }
  36. }

CountryService从 CSV 文件读取数据。

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

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

  1. if (is == null) {
  2. return Optional.empty();
  3. }

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

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

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

  1. try (var br = new BufferedReader(
  2. new InputStreamReader(is, StandardCharsets.UTF_8))) {
  3. CsvToBean<Country> csvToBean = new CsvToBeanBuilder<Country>(br)
  4. .withType(Country.class)
  5. .withMappingStrategy(strategy)
  6. .withIgnoreLeadingWhiteSpace(true)
  7. .build();
  8. countries = csvToBean.parse();
  9. }

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

com/zetcode/web/ReadCountries.java

  1. package com.zetcode.web;
  2. import com.zetcode.bean.Country;
  3. import com.zetcode.service.CountryService;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. import java.util.List;
  11. import java.util.Optional;
  12. @WebServlet(name = "ReadCountries", urlPatterns = {"/read"})
  13. public class ReadCountries extends HttpServlet {
  14. @Override
  15. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  16. throws ServletException, IOException {
  17. response.setContentType("text/html;charset=UTF-8");
  18. Optional<List<Country>> countries = CountryService.getListOfCountries();
  19. String templateName;
  20. if (countries.isPresent()) {
  21. request.setAttribute("countries", countries.get());
  22. templateName = "listCountries.jsp";
  23. } else {
  24. templateName = "showError.jsp";
  25. }
  26. var dispatcher = request.getRequestDispatcher(templateName);
  27. dispatcher.forward(request, response);
  28. }
  29. }

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

webapp/listCountries.jsp

  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>
  2. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  3. <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
  4. <!DOCTYPE html>
  5. <html>
  6. <head>
  7. <meta charset="UTF-8">
  8. <title>Countries</title>
  9. <style>
  10. .marked { color: chocolate }
  11. </style>
  12. </head>
  13. <body>
  14. <table>
  15. <thead>
  16. <tr>
  17. <th>Country</th>
  18. <th>Population</th>
  19. </tr>
  20. </thead>
  21. <tbody>
  22. <c:forEach items="${countries}" var="count">
  23. <c:if test="${count.population > 100000000}">
  24. <tr class="marked">
  25. <td>
  26. <c:out value="${count.name}"/>
  27. </td>
  28. <td>
  29. <fmt:formatNumber type="number" value="${count.population}" />
  30. </td>
  31. </tr>
  32. </c:if>
  33. <c:if test="${count.population < 100000000}">
  34. <tr>
  35. <td>
  36. <c:out value="${count.name}"/>
  37. </td>
  38. <td>
  39. <fmt:formatNumber type="number" value="${count.population}" />
  40. </td>
  41. </tr>
  42. </c:if>
  43. </c:forEach>
  44. </tbody>
  45. </table>
  46. </body>
  47. </html>

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

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

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

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

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

  1. <c:if test="${count.population > 100000000}">
  2. <tr class="marked">
  3. <td>
  4. <c:out value="${count.name}"/>
  5. </td>
  6. <td>
  7. <fmt:formatNumber type="number" value="${count.population}" />
  8. </td>
  9. </tr>
  10. </c:if>

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

webapp/index.jsp

  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>List countries</title>
  7. </head>
  8. <body>
  9. <a href="read">List countries</a>
  10. </body>
  11. </html>

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

webapp/showError.jsp

  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>Error</title>
  7. </head>
  8. <body>
  9. <p>No countries found</p>
  10. </body>
  11. </html>

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

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

您可能也对以下相关教程感兴趣: Java 教程Jersey 应用中的 Web URLJava 验证教程OpenCSV 教程

列出所有 Java servlet 教程