6. 文件的上传和下载
6.1文件上传
常见应用:个人头像上传,商品图片等二进制的资源。
6.1.1 文件上传的原理
所谓的文件上传就是服务器端通过request对象获取输入流,将浏览器端上传的数据读取出来,保存到服务器端
客户端浏览器注意事项:
1.请求方式必须是 post
2.需要使用组件
3.表单必须设置enctype=“multipart/form-data” 表名请求数据使用二进制发送
服务器端处理:
通过request对象,获取InputStream, 可以将浏览器提交的所有数据读取到。
前端上传示例:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="UpLoadServlet" method="post" enctype="multipart/form-data">
<input type="text" name="text"/>
<br/>
<input type="file" name="fileName"/>
<br/>
<input type="submit" value="上传文件"/>
</form>
</body>
</html>
服务端接收文件:
1. 使用开源框架——commons-upload
简介:
Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。 使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io,
commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持
关键类:
DiskFileItemFactory:文件上传的工厂类----支撑jar包:commons-fileupload
ServletFileUpload:处理文件上传的类(调用有参构造放入DiskFileItemFactory对象获取)----支撑jar包:commons-fileupload
**IOUtils **:文件上传的IO流工具类----支撑jar包:commons-io
BeanUtils:存储表单内文本的工具类----支撑jar包:commons-beanutils
使用示例:
@WebServlet("/UpLoadServlet")
public class UpLoadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//文件上传
//创建文件上传类的工厂类的对象
DiskFileItemFactory factory = new DiskFileItemFactory();
//创建文件上传的类
ServletFileUpload upload = new ServletFileUpload(factory);
//解析请求
try {
//表单有几个数据,此集合就有几个FileItem
List<FileItem> list = upload.parseRequest(request);
for(FileItem fileItem : list){
if(fileItem.isFormField()){//文本内容
//获取数据的name
String fieldName = fileItem.getFieldName();
//获取数据的value值
String value = fileItem.getString("UTF-8");
System.out.println(fieldName + " -- " + value);
}else{//二进制内容 -- 文件
//获取上传文件的名字
String name = fileItem.getName();
//获取部署路径
String path = this.getServletContext().getRealPath("/upload");
File file = new File(path);
if(!file.exists()){//文件夹不存在
file.mkdirs();//创建文件夹
}
InputStream in = fileItem.getInputStream();
FileOutputStream out = new FileOutputStream(new File(file,name));
IOUtils.copy(in,out);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
解决中文乱码问题:
获取标签中的非文件数据时,设置编码格式。String value = fileItem.getString(“UTF-8”);
获取文件的文件名存在中文:upload.setHeaderEncoding(“UTF-8”);
2.通过注解@MultipartConfig
将上传的文件保存在Part实例中,可以通过request对象根据上传的表单控件的name获取对应的part对象,同时也支持多个文件上传。通过part对象可直接得到文件的输入流inputStream,在通过outputStream写到本地 。
@WebServlet("/UpLoadServlet02")
@MultipartConfig
public class UpLoadServlet02 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("text");
System.out.println(name);
Part fileName = request.getPart("fileName");
InputStream in = fileName.getInputStream();
String serverpath = request.getServletContext().getRealPath("/upload");
File file = new File(serverpath);
//判断路径文件夹是否存在,不存在就创建。
if (!file.exists()){
file.mkdirs();
}
FileOutputStream out = new FileOutputStream(serverpath+"\\photo01.jpg");
byte[] bs = new byte[1024];
int len;
while ((len=in.read(bs))!=-1){
out.write(bs, 0, len);
}
in.close();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
拓展:在多次上传文件后避免文件重名而覆盖,可以使用在文件名前加上UUID。
UUID uuid = UUID.randomUUID();
拓展示例:
使用开源框架commons-upload实现一个注册的Servlet(包含头像图片上传)。
注:
1.在数据库中存储头像文件的相对地址。
2.图片文件存储在项目部署文件下的头像(head)/用户名(username)文件夹下。
前端(JSP)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
<h1>学生注册页面</h1>
<form action="StuRegisterServlet" method="post" enctype="multipart/form-data">
账号:<input name="username" type="text"/><br/>
密码:<input name="password" type="password"/><br/>
姓名:<input name="name" type="text"/><br/>
年龄:<input name="age" type="number"/><br/>
性别:
<input name="sex" value="man" type="radio" checked="checked"/>男
<input name="sex" value="woman" type="radio"/>女
<br/>
爱好:
<input name="hobby" value="football" type="checkbox" />足球
<input name="hobby" value="basketball" type="checkbox" />篮球
<input name="hobby" value="shop" type="checkbox" />购物
<br/>
上传头像:<input type="file" name="photo"/>
<br/>
<input type="submit" value="注册"/>
</form>
</body>
</html>
Servlet
@WebServlet("/StuRegisterServlet")
public class StuRegisterServlet extends HttpServlet {
private InputStream in;
private FileOutputStream out;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Student stu = getStu(request);
//判断账号是否冲突 true--不存在,false--存在
boolean bool = true;
ArrayList<Student> students = DBUtil.commonQuery(Student.class, "select * from student where username = ?",stu.getUsername());
if(students.size()!=0){
bool = false;
}
if (bool){
try {
DBUtil.commUpdate("INSERT INTO student(username,password,age,name,sex,hobby,photo) VALUES(?,?,?,?,?,?,?);",stu.getUsername(),stu.getPassword(),stu.getAge(),stu.getName(),stu.getSex(),stu.getHobby(),stu.getPhoto());
} catch (SQLException e) {
e.printStackTrace();
}
IOUtils.copy(in,out);
response.sendRedirect("welcome.html");
}else{
System.out.println("账号已经存在");
request.setAttribute("msg","账号已经存在");
request.getRequestDispatcher("register.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
//获取请求中数据,并放入学生对象中
public Student getStu(HttpServletRequest request){
//创建文件上传类的工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//创建文件上传类的对象
ServletFileUpload upload = new ServletFileUpload(factory);
//解析请求
try {
List<FileItem> list = upload.parseRequest(request);
String username = null;
HashMap<String, Object> map = new HashMap<>();
for(FileItem fileItem:list){
if(fileItem.isFormField()){//文本数据
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");
if("username".equals(name)){
username = value;
}
Object v = map.get(name);
if(v == null){//第1次添加
map.put(name,value);
}else{
map.put(name, v + "," + value);
}
}else{//文件数据
String photo = request.getServletContext().getRealPath("/head") + "\\" + username;
System.out.println(photo);
File file = new File(photo);
if(!file.exists()){//文件夹不存在
file.mkdirs();//创建文件夹
}
photo = photo + "\\photo.jpg";
in = fileItem.getInputStream();
out = new FileOutputStream(photo);
map.put("photo",username + "\\photo.jpg");
}
}
Student stu = new Student();
BeanUtils.populate(stu,map);//将map中的数据映射到学生对象中
return stu;
} catch (FileUploadException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
6.2文件下载
web项目大部分文件下载不需要写代码实现,只需通过超链接即可实现,就是通过超链接,在连接地址里写上文件的路径,浏览器会自动打开该文件,如果是普通的文本,图片等浏览器能直接显示内容的浏览器都能直接打开并显示,但如果是浏览器无法打开的文件,比如rar,docx,exe等等,那么浏览器就会提示你下载改文件或者使用当前系统自带的工具打开该文件。
<body>
<a href="download/1.jpg">下载图片</a>
<a href="download/2.zip">压缩包下载</a>
</body>
如果需要实现点击就下载(图片等浏览器可以打开的文件)可以单独实现一个Servlet。
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//文件下载
//获取图片路径
String path = this.getServletContext().getRealPath("/download\\1.jpg");
//截取图片名
String fileName = path.substring(path.lastIndexOf("\\") + 1);
//设置编码格式(如果需要下载的文件名中有中文)
fileName = URLEncoder.encode(fileName,"UTF-8");
//设置响应头信息(通过头部信息告诉浏览器,这是传到浏览器需要下载的文件)
response.setHeader("Content-disposition", "attachment;fileName="+fileName);
//通过IO流传输到浏览器
FileInputStream in = new FileInputStream(path);
ServletOutputStream out = response.getOutputStream();
byte[] bs = new byte[1024];
int len;
while ((len=in.read(bs))!=-1){
out.write(bs,0,len);
}
//关流
in.close();
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}