文章目录
一、课程目标
【理解】servlet文件上传原理
【理解】servlet基于jar包文件上传
【掌握】servelt3.0注解形式的文件上传
【理解】多文件上传
【了解】文件下载原理
【理解】LayUi文件上传组件的使用
二、基于Jar文件上传
2.1 原理
将客户端的文件以流的形式进行解析发送至服务器端并进行保存的过程称之为文件上传,很多前端框架都定义了这种上传的功能,大致分为几类,form表单形式,ajax形式
创建DiskFileItemFactory工厂对象,创建文件解析对象ServletFileUpload传入工厂对象,调用解析方法解析请求中的流数据,将流数据以FileItem集合的形式进行返回(包含普通字段数据以及文件字段数据的集合),遍历集合判断是否为文件字段,如果是文件字段获取输入流,通过输出流存储至指定文件中(getName上传文件名),如果获取普通字段数据通过(getFieldName,getString)相应方法获取数据信息
2.2 书写
如果是基于form表单形式,那么在提交数据时需要修改表单的数据提交方式,将表达的数据以流的形式发送至服务器端,并且修改请求方式为post,否则不能进行上传
1、书写form表单 修改提交方式method=“post”,数据发送方式enctype="multipart/form-data“这样提交的文件会在请求体中以流的形式发送至客户端
<!-- 提交方式post 提交数据形式multipart/form-data -->
<form action="/upload" method="post" enctype="multipart/form-data">
2、创建相应的文件域用于选择提交的文件
<form action="upload" method="post" enctype="multipart/form-data">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
头像:<input type="file" name="img" ><br>
<input type="submit" value="注册">
</form>
3、在导入相应jar包后创建对应处理文件上传的servlet类
package com.yunhe.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
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 org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 创建文件解析工厂对象
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 使用文件解析工厂对象创建对应的解析对象
ServletFileUpload servletUpload = new ServletFileUpload(fileItemFactory);
// 创建map集合保存普通数据字段
HashMap<String, String> map = new HashMap<>();
try {
// 解析请求中的流数据封装保存至对应的字段数据对象中
List<FileItem> fileItems = servletUpload.parseRequest(request);
// 遍历字段数据集合 判断数据类型进行操作
for (FileItem fileItem : fileItems) {
// 如果当前遍历的是form表单的普通字段
if (fileItem.isFormField()) {
map.put(fileItem.getFieldName(), fileItem.getString());
} else {
// 获取上传原文件名
String name = fileItem.getName();
// 创建保存文件的路径
String contextPath = getServletContext().getRealPath("/");
System.out.println(contextPath);
// 创建保存文件的file类
File file = new File(contextPath, name);
// 在指定路径创建文件对象
//file.createNewFile();
// 读取上传文件的输入流
InputStream is = fileItem.getInputStream();
// 创建写入文件的输出流
FileOutputStream fos = new FileOutputStream(file);
// 读取输入流数据写入文件
byte[] b = new byte[1024];
int len = 0;
while ((len = is.read(b)) != -1) {
fos.write(b, 0, len);
}
is.close();
fos.flush();
fos.close();
System.out.println("文件被保存到:" + file);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3 多文件上传
由于后台进行处理时,就是将请求的所有数据进行解析,所以无论前台发送多个文件,后台解析方式不变,所以无需修改后台代码,只需要在前台上传页面对应文件域中添加复选属性multiple即可
<form action="upload" method="post" enctype="multipart/form-data">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
头像:<input type="file" name="img" ><br>
<input type="submit" value="注册" multiple>
</form>
三、基于注解文件上传
3.1 原理
将客户端的文件以流的形式进行解析发送至服务器端并进行保存的过程称之为文件上传,很多前端框架都定义了这种上传的功能,大致分为几类,form表单形式,ajax形式
在servlet3.0之后可以将数据封装为part对象可以直接获取上传的文件数据,需要将servlet使用@MultipartConfig进行标识
2.2 书写
前台代码无需修改
package com.yunhe.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.util.Collection;
import java.util.UUID;
@WebServlet("/upload")
//告诉服务器 这个servlet可以用来直接接收文件
@MultipartConfig
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
//可以直接通过name获取请求中的值
String username = request.getParameter("username");
// Part part = request.getPart("imgFile");
//获取提交文件的名字(单个文件)
// String submittedFileName = part.getSubmittedFileName();
//获取前台请求的所有文件(包含普通字段在内)
Collection<Part> parts = request.getParts();
for (Part p : parts) {
//普通字段封装为part对象数据为null
if(p.getSubmittedFileName()!=null){
InputStream inputStream = p.getInputStream();
BufferedInputStream bis=new BufferedInputStream(inputStream);
File file=new File("F://upload", UUID.randomUUID()+p.getSubmittedFileName());
FileOutputStream fos=new FileOutputStream(file);
byte [] b=new byte[2048];
int len=0;
while((len=bis.read(b))!=-1){
fos.write(b,0,len);
}
bis.close();
fos.flush();
fos.close();
}
}
}
}
四、文件下载
4.1 原理
就是服务器端根据客户端请求查找指定文件,通过io流的形式将文件发送至客户端,但是由于数据的类型,需要修改响应头变为文件形式,否则会直接输出到客户端展示
4.2 书写
1、创建响应客户端的文件对象
2、设置响应头,设置文件名称
3、获取响应输出流读取文件输出至客户端
package com.yunhe.servlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class DownLoadServlet
*/
@WebServlet("/download")
public class DownLoadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
File file = new File("C://Users/Administrator/Desktop/test.doc");
//设置相应头设置响应文件名
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
FileInputStream fis=new FileInputStream(file);
ServletOutputStream os = response.getOutputStream();
byte b[]=new byte[1024];
int len=0;
while((len=fis.read(b))!=-1){
os.write(b, 0, len);
}
fis.close();
os.flush();
os.close();
}
}
五、LayUI文件上传组件
通过layui加载相应模块为指定标签添加上传功能(无需使用input标签就可以完成文件上传)
5.1 使用
1、在页面引入layUI相关样式css与js
2、加载指定模块
3、书写相应配置属性绑定页面元素
4、后台进行文件的保存(注意返回数据形式)接口返回的相应信息必须是一个标准的 JSON 格式
5.2 配置属性
参数选项 | 说明 | 类型 | 默认值 |
---|---|---|---|
elem | 指向容器选择器,如:elem: ‘#id’。也可以是DOM对象 | string/object | - |
url | 服务端上传接口,返回的数据规范请详见下文 | string | - |
data | 请求上传接口的额外参数。如:data: {id: ‘xxx’} | object | - |
headers | 接口的请求头。如:headers: {token: ‘sasasas’}。注:该参数为 layui 2.2.6 开始新增 | ||
accept | 指定允许上传时校验的文件类型,可选值有:images(图片)、file(所有文件)、video(视频)、audio(音频) | string | images |
acceptMime | 规定打开文件选择框时,筛选出的文件类型,值为用逗号隔开的 MIME 类型列表。如: acceptMime: ‘image/*’(只显示图片文件) acceptMime: ‘image/jpg, image/png’(只显示 jpg 和 png 文件) 注:该参数为 layui 2.2.6 开始新增 | string | images |
exts | 允许上传的文件后缀。一般结合 accept 参数类设定。假设 accept 为 file 类型时,那么你设置 exts: ‘zip|rar|7z’ 即代表只允许上传压缩格式的文件。如果 accept 未设定,那么限制的就是图片的文件格式 | string | jpg|png|gif|bmp|jpeg |
auto | 是否选完文件后自动上传。如果设定 false,那么需要设置 bindAction 参数来指向一个其它按钮提交上传 | boolean | true |
bindAction | 指向一个按钮触发上传,一般配合 auto: false 来使用。值为选择器或DOM对象,如:bindAction: ‘#btn’ | string/object | - |
field | 设定文件域的字段名 | string | file |
size | 设置文件最大可允许上传的大小,单位 KB。不支持ie8/9 | number | 0(即不限制) |
multiple | 是否允许多文件上传。设置 true即可开启。不支持ie8/9 | boolean | false |
number | 设置同时可上传的文件数量,一般配合 multiple 参数出现。 注意:该参数为 layui 2.2.3 开始新增 | number | 0(即不限制) |
drag | 是否接受拖拽的文件上传,设置 false 可禁用。不支持ie8/9 | boolean | true |
回调 | |||
choose | 选择文件后的回调函数。返回一个object参数,详见下文 | function | - |
before | 文件提交上传前的回调。返回一个object参数(同上),详见下文 | function | - |
done | 执行上传请求后的回调。返回三个参数,分别为:res(服务端响应信息)、index(当前文件的索引)、upload(重新上传的方法,一般在文件上传失败后使用)。详见下文 | function | - |
error | 执行上传请求出现异常的回调(一般为网络异常、URL 404等)。返回两个参数,分别为:index(当前文件的索引)、upload(重新上传的方法)。详见下文 | function | - |
upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="static/layui/css/layui.css">
<script src="static/layui/layui.js"></script>
<script src="static/js/jquery-3.4.1.min.js"></script>
<script>
$(function () {
layui.use('upload', function () {
var upload = layui.upload;
//执行实例
var uploadInst = upload.render({
elem: '#test1' //绑定元素
, url: 'imgUpload' //上传接口
, done: function (res) {
//上传完毕回调
// console.log(res.data.src)
// alert("上传成功")
$("#i1").attr("src",res.data.src)
}
, error: function () {
//请求异常回调
alert("上传失败")
}
});
});
})
</script>
</head>
<body>
<img id="i1">
<button type="button" class="layui-btn" id="test1">
<i class="layui-icon"></i>上传图片
</button>
</body>
</html>
ImgServlet.java
package com.yunhe.servlet;
import net.sf.json.JSONObject;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.util.Collection;
import java.util.UUID;
@WebServlet("/imgUpload")
@MultipartConfig
public class ImgServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
Collection<Part> parts = request.getParts();
for (Part p : parts) {
//普通字段封装为part对象数据为null
if (p.getSubmittedFileName() != null) {
InputStream inputStream = p.getInputStream();
BufferedInputStream bis = new BufferedInputStream(inputStream);
String fileName=UUID.randomUUID() + p.getSubmittedFileName();
File file = new File("F://upload", fileName);
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[2048];
int len = 0;
while ((len = bis.read(b)) != -1) {
fos.write(b, 0, len);
}
bis.close();
fos.flush();
fos.close();
//创建json对象
JSONObject jsonObject=new JSONObject();
JSONObject dataJson=new JSONObject();
jsonObject.put("code",0);
jsonObject.put("msg","");
dataJson.put("src","/img/"+fileName);
jsonObject.put("data",dataJson);
System.out.println(jsonObject);
response.getWriter().print(jsonObject);
}
}
}
}