servlet 从 WAR 内读取 CSV 文件
在本教程中,我们从WEB-INF
目录中的 CSV 文件读取数据。 我们使用 servlet,JSP 文件和 JSTL 库。 Web 应用已部署在 Jetty 上。 OpenCSV 库用于读取 CSV 数据。
CSV
CSV(逗号分隔值)格式是在电子表格和数据库中使用的非常流行的导入和导出格式。
在以下 Web 应用中,我们从 WAR 文件中的 CSV 文件读取数据,并将数据显示在网页中。 标记人口超过一亿的国家。
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
<?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
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
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
。
@CsvBindByName
private String name;
@CsvBindByName
将name
属性映射到Name
列中的字段。
com/zetcode/CountryService.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 文件读取数据。
try (InputStream is = CountryService.class.getClassLoader()
.getResourceAsStream("countries.csv")) {
我们使用getResourceAsStream()
方法将InputStream
转换为countries.csv
文件。
if (is == null) {
return Optional.empty();
}
如果未打开输入流,则返回空Optional
。 这用于避免null
值。
HeaderColumnNameMappingStrategy<Country> strategy
= new HeaderColumnNameMappingStrategy<>();
strategy.setType(Country.class);
我们使用 OpenCSV 的HeaderColumnNameMappingStrategy
将Country
bean 映射到 CSV 文件中的行。 每行都转换为一个 bean。 映射是在@CsvBindByName
注解的帮助下完成的。
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
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
<%@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 表中显示数据。
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
我们使用两个 JSTL 标签库:核心库和格式库。
<c:forEach items="${countries}" var="count">
使用<c:forEach>
标签,我们遍历countries
对象。
<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
<%@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
<%@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 教程, Jersey 应用中的 Web URL , Java 验证教程和 OpenCSV 教程。