⽂件上传下载和三层架构
⽂件上传下载和三层架构
⼀、⽂件上传
method 需要使⽤ post 提交,get 限制了数据⼤⼩。
enctype 需使⽤ multipart/form-data,不然直接报错(需要⼆进制数据)。
需要提供 fifile 控件。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<span >${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、代码⽰例
@WebServlet("/fileUpload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 普通控件数据还是使⽤ getParameter ⽅法来获取
System.out.println("username:" + Parameter("username"));
// ⽂件控件数据获取
Part part = Part("headImg");
// 保存到磁盘上
part.write("D:/headImg.png");
}
}
三、⽂件上传细节
1. 获取上传⽂件名
以前是拷贝⽂件(⾃传⾃存),知道⽂件类型;现在是⽤户传,程序接收,接收到⽂件时,涉及保存到磁盘使⽤什么⽂件名以及⽂件类型的问题,所有需要先获取到⽂件名及⽂件类型。可使⽤ Part API 获取:
返回值⽅法作⽤
String getHeader("contentdisposition")Tocmat 8.0 之前使⽤通过请求头获取⽂件名,需截取字符串
String getSubmittedFileName()Tomcat8.0 之后提供的直接获取⽂件名⽅式
2. ⽂件名相同覆盖现有⽂件
若上传得⽂件名相同会导致覆盖服务器之前已上传的的⽂件,咱们的解决⽅法就是⾃⼰给⽂件起⼀个唯⼀的名称,确保不被覆盖,这⾥我们使⽤的是UUID。
// ⽂件控件数据获取
Part part = Part("headImg");
// 获取上传⽂件名
String realFileName = SubmittedFileName();
// 获取上传⽂件扩展名
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 的代码如下:
@WebServlet("/fileUpload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/
/ ⽂件控件数据获取
Part part = Part("headImg");
// 获取上传⽂件名
String realFileName = SubmittedFileName();
// 获取上传⽂件扩展名
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)
调整项⽬部署⽅式为 External Source
以上操作之后可以获取到项⽬下的 upload ⽬录了,但是之前的重新部署⽆效了,往后可使⽤编译类的⽅式来代替重新部署。
4. ⽂件类型约束问题
UploadServlet.java
@WebServlet("/fileUpload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ⽂件控件数据获取
Part part = Part("headImg");
// 判断上传的⽂件类型合法不
if(!ContentType().startsWith("img/")) {
req.setAttribute("errorMsg", "请上传图⽚");
}
String realFileName = SubmittedFileName();
// 获取⽂件扩展名
String ext = realFileName.substring(realFileName.lastIndexOf("."));
// ⽣成唯⼀字符串拼接⽂件名
String fileName = UUID.randomUUID().toString() + ext;
// 获取项⽬下的 upload ⽬录的绝对路径,拼接成⽂件的保存路径
String realPath = getServletContext().getRealPath("/upload") +"/"+ fileName;
// 保存到磁盘上
part.write(realPath);
}
}
register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<span >${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
<%@ 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>
2. ⽂件下载限制
下载功能已经实现,但是⽂件放在 WEB-INF 外⾯不安全,⽤户只需要拿到下载的超链接都能够下载,实际开发中,我们的⽂件通常需要⽤户有⼀定的权限才能下载,所以⽂件应该放在 WEB-INF 下,这样的话,⽤户就不可以直接访问到了,须请求到交由 Servlet 来处理,我们就可以在其 service ⽅法中编写下载限制操作。
2.1 移动下载资源WEB-INF下
2.2 修改 download.jsp 修改下载资源的链接地址 bar
<%@ 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
根据请求传递⽂件名参数获取对应⽂件响应给浏览器。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取⽤户想要下载的⽂件名称
String fileName = Parameter("fileName");
//获取浏览器类型
String header = Header("User-Agent");
//根据浏览器类型设置下载⽂件名
//三⽬运算法
String mozilla = ains("Mozilla") ? de(fileName, "UTF-8") : new Bytes("UTF-8"), "ISO-8859-1");
//设置下载⽂件名
resp.setHeader("Content-Disposition", "attachment;filename=" + mozilla);
//获取⽂件所在跟路径
String realPath = ServletContext().getRealPath("/WEB-INF/download/");
//使⽤⼯具类File的copy⽅法获取⽂件输⼊流,响应会浏览器
//py(源⽂件,输出)
// Path path = ("C:/", "Xmp");Path⽤于来表⽰⽂件路径和⽂件。可以有多种⽅法来构造⼀个Path对象来表⽰⼀个⽂件路径,或者⼀个⽂件:
//OutputStream()响应⼀个输出流,作⽤下载来⽤
<(realPath, fileName), OutputStream());
}
3. 下载⽂件名称问题(能使⽤即可)
默认情况下,Tomcat 服务器未告知浏览器⽂件的名称,所以需要⼿动设置响应头来告知浏览器⽂件名称,⽅法如下:
// ⽆需记,知道需要设置即可 // 给浏览器⼀个推荐名称
resp.setHeader("Content-Disposition", "attachment;filename=⽂件名称");
处理中⽂件名称的问题
IE 使⽤ URL 编码⽅式:de(fifileName, "UTF-8")
⾮ IE使⽤ ISO-8859-1 编码:new String (Bytes("UTF-8"), "ISO-8859-1")
具体代码见上⾯⼀份
三层架构
1.三层架构介绍
Web 开发中的最佳实践:分层开发模式(技术层⾯的"分⽽治之")。三层架构(3-tier architecture):通常意义上的三层架构就是将整个业务应⽤划分为:表现层、业务逻辑层、数据访问层。区分层次的⽬的即为了“⾼内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的⼀种结构。
表现层(Predentation Layer):MVC,负责处理与界⾯交互的相关操作。
html获取input输入的数据业务层(Business Layer):Service,负责复杂的业务逻辑计算和判断。
持久层(Persistent Layer):DAO,负责将业务逻辑数据进⾏持久化存储
2. 业务层命名规范
包名:
公司域名倒写.模块名.service:存放业务接⼝代码。
公司域名倒写.模块名.service.impl:存放业务层接⼝的实现类。
类名:
IXxxService:业务层接⼝,Xxx 表⽰对应模型,⽐如操作 User 的就起名为 IUserService。
XxxServiceImpl:业务层接⼝对应的实现类,⽐如操作 User 的就起名为 UserServiceImpl。
XxxServiceTest:业务层实现的测试类。