文件上传下载和三层架构 三层架构

一 、文件上传

  • method 需要使用 post 提交,get 限制了数据大小。
  • enctype 需使用 multipart/form-data,不然直接报错(需要二进制数据)。
  • 需要提供 fifile 控件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jsp复制代码<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<span style="color: red">${errorMsg}</span><br/>
<form action="/fileType" method="post" enctype="multipart/form-data">
<p>账号:<input type="text" name="username"/></p>
<%--input 便签 type选择file 即选择了一个上传文件控件--%>
<p>头像:<input type="file" name="Default"/></p>
<input type="submit" value="注册">
</form>
</body>
</html>

注意: enctype=”multipart/form-data” 提交的数据,getParameter() 无法获取到。

二、Servlet3.0 文件上传

1. API

HttpServletRequest 提供了两个方法用于从请求中解析上传的文件

返回值 方法 作用
Part getPart(String name) 用于获取请求中指定 name 的文件
Collection getParts() 获取请求中全部的文件

Part 中常用方法:

返回值 方法 作用
void write(String fifileName) 直接把接收到的文件保存到磁盘中
void getContentType() 获取文件的类型 MIME
String getHeader(String name) 获取请求头信息
long getSize() 获取文件的大小

​ 要给 Servlet 贴一个标签 @MultipartConfig 然后使用 getPart() 获取请求中指定 name 的文件到 Part 对象中,再使用 write 方法把文件保存到指定目录就可以了

2、代码示例

1
2
3
4
5
6
7
8
9
10
11
12
java复制代码@WebServlet("/fileUpload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 普通控件数据还是使用 getParameter 方法来获取
System.out.println("username:" + req.getParameter("username"));
// 文件控件数据获取
Part part = req.getPart("headImg");
// 保存到磁盘上
part.write("D:/headImg.png");
}
}

三、文件上传细节

1. 获取上传文件名

​ 以前是拷贝文件(自传自存),知道文件类型;现在是用户传,程序接收,接收到文件时,涉及保存到磁盘使用什么文件名以及文件类型的问题,所有需要先获取到文件名及文件类型。可使用 Part API 获取:

返回值 方法 作用
String getHeader(“contentdisposition”) Tocmat 8.0 之前使用通过请求头获取文件名,需截取字符串
String getSubmittedFileName() Tomcat8.0 之后提供的直接获取文件名方式

2. 文件名相同覆盖现有文件

​ 若上传得文件名相同会导致覆盖服务器之前已上传的的文件,咱们的解决方法就是自己给文件起一个唯一的名称,确保不被覆盖,这里我们使用的是 UUID。

1
2
3
4
5
6
7
8
9
10
java复制代码// 文件控件数据获取
Part part = req.getPart("headImg");
// 获取上传文件名
String realFileName = part.getSubmittedFileName();
// 获取上传文件扩展名
String ext = realFileName.substring(realFileName.lastIndexOf("."));
// 生成唯一字符串拼接文件名
String fileName = UUID.randomUUID().toString() + ext;
// 保存到磁盘上
part.write("D:/" + fileName);

3. 文件保存位置问题

​ 文件在磁盘某个位置,不在项目下,无法使用 HTTP 协议访问,所以要把用户上传的文件存放到项目中才可通过 HTTP 协议来访问,且保存的位置路径不可以写绝对路径,那么怎么办?可以通过 ServletContext 对象的 getRealPath(“项目中保存上传文件的文件夹的相对路径”) 来获取其的绝对路径。

​ 在项目的 web 目录下新建一个名为 upload 文件夹,修改 UploadServlet.java 的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java复制代码@WebServlet("/fileUpload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 文件控件数据获取
Part part = req.getPart("headImg");
// 获取上传文件名
String realFileName = part.getSubmittedFileName();
// 获取上传文件扩展名
String ext = realFileName.substring(realFileName.lastIndexOf("."));
// 生成唯一字符串拼接文件名
String fileName = UUID.randomUUID().toString() + ext;
// 获取项目下的 upload 目录的绝对路径,拼接成文件的保存路径
String realPath = getServletContext().getRealPath("/upload") +"/"+ fileName;
// 保存到磁盘上
part.write(realPath);
}
}

3.1 无法获取项目下的 upload 目录

以上方式没什么效果,原因是 IDEA 工具使用 打包 web 项目(war) 的方式来部署,所以位置有偏 差,需要还原 Web 项目的原本目录结构,以及调整部署方式。调整步骤如下:

  • WEB-INF 下 创建 classes 目录,用于存放 class 文件
  • 修改项目 class 文件输出位置到 /WEB-INF/class 中(File -> Project Structure)
    1635603531088.png
  • 调整项目部署方式为 External Source

1635603560838.png

以上操作之后可以获取到项目下的 upload 目录了,但是之前的重新部署无效了,往后可使用编译类的 方式来代替重新部署。

1635603576349.png


4. 文件类型约束问题

UploadServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码@WebServlet("/fileUpload") 
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 文件控件数据获取
Part part = req.getPart("headImg");
// 判断上传的文件类型合法不
if(!part.getContentType().startsWith("img/")) {
req.setAttribute("errorMsg", "请上传图片");
req.getRequestDispatcher("/register.jsp").forward(req, resp); return;
}
String realFileName = part.getSubmittedFileName();
// 获取文件扩展名
String ext = realFileName.substring(realFileName.lastIndexOf("."));
// 生成唯一字符串拼接文件名
String fileName = UUID.randomUUID().toString() + ext;
// 获取项目下的 upload 目录的绝对路径,拼接成文件的保存路径
String realPath = getServletContext().getRealPath("/upload") +"/"+ fileName;
// 保存到磁盘上
part.write(realPath);
}
}

register.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
jsp复制代码<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<span style="color: red">${errorMsg}</span><br/>
<form action="/fileUpload" method="post" enctype="multipart/form-data">
<p>账号:<input type="text" name="username"/></p>
<p>头像:<input type="file" name="headImg"/></p>
<input type="submit" value="注册">
</form>
</body>
</html>

5. 文件大小约束问题

文件上传限制大小可提高服务器硬盘的使用率,防止用户恶意上传文件造成服务器磁盘资源紧张。可以 通过设置 @MutipartConfifig 的属性做限制,其属性如下:

  • maxFileSize:单个上传文件大小限制,单位:bytes
  • maxRequestSize:显示请求中数据的大小,单位:bytes

四、文件下载

将服务器中的资源下载保存到用户电脑中。

1. 文件下载的简单实现

在 web 下新建 download 目录,里面提供两个资源 dog.rar 和 猫.rar

1
2
3
4
5
6
7
8
9
10
11
jsp复制代码<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>下载</title>
</head>
<body>
<h3>文件下载</h3>
<a href="/download/dog.rar">dog.rar</a><br/>
<a href="/download/猫.rar">猫.rar</a><br/>
</body>
</html>

1635605012516.png

2. 文件下载限制

​ 下载功能已经实现,但是文件放在 WEB-INF 外面不安全,用户只需要拿到下载的超链接都能够下载, 实际开发中,我们的文件通常需要用户有一定的权限才能下载,所以文件应该放在 WEB-INF 下,这样 的话,用户就不可以直接访问到了,须请求到交由 Servlet 来处理,我们就可以在其 service 方法中编 写下载限制操作。

2.1 移动下载资源WEB-INF下

image.png

2.2 修改 download.jsp 修改下载资源的链接地址 bar

1
2
3
4
5
6
7
8
9
10
11
jsp复制代码<%@ page contentType="text/html;charset=UTF-8" language="java" %> 
<html>
<head>
<title>下载</title>
</head>
<body>
<h3>文件下载</h3>
<a href="/download?fileName=dog.rar">dog.rar</a><br/>
<a href="/download?fileName=猫.rar">猫.rar</a><br/>
</body>
</html>

2.3 编写 DownloadServlet.java

根据请求传递文件名参数获取对应文件响应给浏览器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取用户想要下载的文件名称
String fileName = req.getParameter("fileName");
//获取浏览器类型
String header = req.getHeader("User-Agent");
//根据浏览器类型设置下载文件名
//三目运算法
String mozilla = header.contains("Mozilla") ? URLEncoder.encode(fileName, "UTF-8") : new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
//设置下载文件名
resp.setHeader("Content-Disposition", "attachment;filename=" + mozilla);

//获取文件所在跟路径
String realPath = req.getServletContext().getRealPath("/WEB-INF/download/");
//使用工具类File的copy方法获取文件输入流,响应会浏览器
//Files.copy(源文件,输出)
// Path path = Paths.get("C:/", "Xmp");Path用于来表示文件路径和文件。可以有多种方法来构造一个Path对象来表示一个文件路径,或者一个文件:
//resp.getOutputStream()响应一个输出流,作用下载来用
Files.copy(Paths.get(realPath, fileName), resp.getOutputStream());


}

3. 下载文件名称问题(能使用即可)

默认情况下,Tomcat 服务器未告知浏览器文件的名称,所以需要手动设置响应头来告知浏览器文件名称,方法如下:

1
2
java复制代码// 无需记,知道需要设置即可 // 给浏览器一个推荐名称 
resp.setHeader("Content-Disposition", "attachment;filename=文件名称");

处理中文件名称的问题

  • IE 使用 URL 编码方式:URLEncoder.encode(fifileName, “UTF-8”)
  • 非 IE使用 ISO-8859-1 编码:new String (fifileName.getBytes(“UTF-8”), “ISO-8859-1”)

具体代码见上面一份

三层架构

1.三层架构介绍

​ Web 开发中的最佳实践:分层开发模式(技术层面的”分而治之”)。三层架构(3-tier architecture):通 常意义上的三层架构就是将整个业务应用划分为:表现层、业务逻辑层、数据访问层。区分层次的目的 即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结

构。

  • 表现层(Predentation Layer):MVC,负责处理与界面交互的相关操作。
  • 业务层(Business Layer):Service,负责复杂的业务逻辑计算和判断。
  • 持久层(Persistent Layer):DAO,负责将业务逻辑数据进行持久化存储

image.png

image.png

image.png

  1. 业务层命名规范

  • 包名:
    • 公司域名倒写.模块名.service:存放业务接口代码。
    • 公司域名倒写.模块名.service.impl:存放业务层接口的实现类。
  • 类名:
    • IXxxService:业务层接口,Xxx 表示对应模型,比如操作 User 的就起名为 IUserService。
    • XxxServiceImpl:业务层接口对应的实现类,比如操作 User 的就起名为 UserServiceImpl。
    • XxxServiceTest:业务层实现的测试类。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%