文件上传下载
文件上传
文件上传的应用
比如个人信息的管理,上传头像
比如商品信息的管理,上传商品的图片
这些都需要通过浏览器客户端将图片上传到服务器的磁盘上
文件上传原理
所谓的文件上传就是服务器端通过request对象获取输入流,将浏览器端上传的数据读取出来,保存到服务器端
客户端浏览器注意事项
- 请求方式必须是 post
- 需要使用组件<input type="file" name="file">
- 表单必须设置enctype="multipart/form-data"
服务器端处理 通过request对象,获取InputStream, 可以将浏览器提交的所有数据读取到.
上传开源框架-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包的支持jar包路径:
保存一个完整的表单信息,除了将文件上传到服务器以外,还需要将其他表单项信息保存到对象,并存储到数据中心,而对应的图片信息则保存为保存的路径即可。
那么怎么获取到其他的表单项数据?item.getFileName(),item.getString()
为了更好完成对其他表单项的值的获取,需要借助另一个开源框架 BeanUtils
它除了自身的jar包,还依赖于commons-logging.jar
文件上传案例
上传单个图片到磁盘
<body>
<!-- enctype: 编码类型,提交的表单为二进制内容:multipart/form-data
默认为文本编码类型: application/x-www-form-urlencoded
注意: 表单中的参数就不能通过getParameter去取值了, 要想取表单中的值需要第三方jar包解析 ;
但是action属性中的参数是可获取到的
-->
<form action="RegisterServlet?action=upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username" /><br/>
密码:<input type="text" name="password" /><br/>
文件上传:<input type="file" name="file" /><br/>
<input type="submit" value="提交" />
</form>
</body>
//版本1:解析图片,存储到D:/b.jpg
private void deal1(HttpServletRequest request) throws IOException, FileNotFoundException {
String action = request.getParameter("action");
if("upload".equals(action)){
//1. 实例化文件上传的工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.从工厂中获取fileUpload的对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//3.通过fileUpload对象解析request
try {
//解析request后,将内容放入第三方提供的FileItem实体类中
List<FileItem> list = fileUpload.parseRequest(request);
//遍历集合,取出FileItem实体类中的数据
for(FileItem fileItem : list){
//System.out.println(fileItem); //打印FileItem实体类对象
//判断是否为文本表单内容
if(!fileItem.isFormField()){ //处理非文本表单内容---处理图片
//-------io流操作-------
InputStream iStream = fileItem.getInputStream();
int len;
byte[] b = new byte[1024];
FileOutputStream fos = new FileOutputStream("d://"+fileItem.getName());
while((len=iStream.read(b))!=-1){
fos.write(b, 0, len);
}
//----------关流s----------
iStream.close();
fos.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
优化版
//版本2:io流的优化;图片的路径要跟着项目走--不能随意指定:d:/a.jpg
private void deal2(HttpServletRequest request) throws IOException, FileNotFoundException {
String action = request.getParameter("action");
if("upload".equals(action)){
//1. 实例化文件上传的工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.从工厂中获取fileUpload的对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//3.通过fileUpload对象解析request
try {
List<FileItem> list = fileUpload.parseRequest(request);
for(FileItem fileItem : list){
if(!fileItem.isFormField()){
//图片的路径要跟着项目走--不能随意指定:d:/a.jpg
//io流的优化
//获取的是项目的部署路径下的upload
String path = getServletContext().getRealPath("upload");
//判断部署的upload路径是否存在,如果不存在则创建
File parent = new File(path);
if(!parent.exists()){
parent.mkdirs(); //创建父目录
}
//拼完整图片的文件路径
File allFile = new File(parent, fileItem.getName());
//------------io流操作:第三方处理-----------
//参数1:上传的文件资源
IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
解决重名的问题
//版本3:处理文件上传的重名问题? uuid,时间
private void deal3(HttpServletRequest request) throws IOException, FileNotFoundException {
String action = request.getParameter("action");
if("upload".equals(action)){
//1. 实例化文件上传的工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.从工厂中获取fileUpload的对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//3.通过fileUpload对象解析request
List<FileItem> list;
try {
list = fileUpload.parseRequest(request);
for(FileItem fileItem : list){
if(!fileItem.isFormField()){
//图片的路径要跟着项目走--不能随意指定:d:/a.jpg
//io流的优化
//获取的是项目的部署路径下的upload
String path = getServletContext().getRealPath("upload");
//处理日期
SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss");
String strDate = sdf.format(new Date());
//判断部署的upload路径是否存在,如果不存在则创建
File parent = new File(path+strDate);
if(!parent.exists()){
parent.mkdirs(); //创建父目录
}
//--处理重命名方式1: uuid
//String uuid = UUID.randomUUID().toString();
//String fileName = uuid+"_"+fileItem.getName();
//File allFile = new File(parent, fileName);
//处理重命名方式2: 日期处理: 2019/3/11/11/38/56/a.jpg 2019/3/11/11/38/57/a.jpg
File allFile = new File(parent, fileItem.getName());
IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
上传文件并使用BeanUtils保存checkbox表单
<body>
<!-- enctype: 编码类型,提交的表单为二进制内容:multipart/form-data
默认为文本编码类型: application/x-www-form-urlencoded
注意: 表单中的参数就不能通过getParameter去取值了, 要想取表单中的值需要第三方jar包解析 ;
但是action属性中的参数是可获取到的
-->
<form action="RegisterServlet?action=upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username" /><br/>
密码:<input type="text" name="password" /><br/>
爱好:<input type="checkbox" name="loves" value="eat" />吃
<input type="checkbox" name="loves" value="drink"/>喝
<input type="checkbox" name="loves" value="play"/>玩<br/>
文件上传:<input type="file" name="file" /><br/>
<input type="submit" value="提交" />
</form>
</body>
//版本4:在后台存储完整数据
private void deal4(HttpServletRequest request)
throws UnsupportedEncodingException, IOException, FileNotFoundException {
//1. 实例化文件上传的工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.从工厂中获取fileUpload的对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//3.通过fileUpload对象解析request
List<FileItem> list;
User user = new User();
Map<String, String> map = new HashMap<>();
try {
list = fileUpload.parseRequest(request);
for(FileItem fileItem : list){
if(fileItem.isFormField()){ //处理文本表单内容
//获取到map集合中key对应的值,如果为空,则进行存储;否则,进行与旧值拼接
String value = map.get(fileItem.getFieldName());
if(value == null){
map.put(fileItem.getFieldName(), fileItem.getString("utf-8"));
}else{
map.put(fileItem.getFieldName(), value+","+fileItem.getString("utf-8"));
}
}else{ //处理二进制内容
//获取的是项目的部署路径下的upload
String path = getServletContext().getRealPath("upload");
//判断部署的upload路径是否存在,如果不存在则创建
File parent = new File(path);
if(!parent.exists()){
parent.mkdirs(); //创建父目录
}
//--处理重命名方式1: uuid
String uuid = UUID.randomUUID().toString();
String fileName = uuid+"_"+fileItem.getName();
File allFile = new File(parent, fileName);
IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
//user.setUrl(allFile.getAbsolutePath()); //存储图片路径
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
//将map的数据注入的User对象,["username":"zs","password":"123"]
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
展示单个图片、展示多个图片
//版本5:展示单个图片、展示多个图片
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
if("upload".equals(action)){
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
List<FileItem> list;
User user = new User();
Map<String, String> map = new HashMap<>();
try {
list = fileUpload.parseRequest(request);
for(FileItem fileItem : list){
if(fileItem.isFormField()){ //处理文本表单内容
//获取到map集合中key对应的值,如果为空,则进行存储;否则,进行与旧值拼接
String value = map.get(fileItem.getFieldName());
if(value == null){
map.put(fileItem.getFieldName(), fileItem.getString("utf-8"));
}else{
map.put(fileItem.getFieldName(), value+","+fileItem.getString("utf-8"));
}
}else{ //处理二进制内容
//获取的是项目的部署路径下的upload
String path = getServletContext().getRealPath("upload");
//判断部署的upload路径是否存在,如果不存在则创建
File parent = new File(path);
if(!parent.exists()){
parent.mkdirs(); //创建父目录
}
//--处理重命名方式1: uuid
String uuid = UUID.randomUUID().toString();
String fileName = uuid+"_"+fileItem.getName();
File allFile = new File(parent, fileName);
IOUtils.copy(fileItem.getInputStream(), new FileOutputStream(allFile));
//注意:此处不能存带盘符的完整路径;应该存相对路径
//user.setUrl(allFile.getAbsolutePath()); //存储图片路径
//需求1: 存储单张图片路径
//user.setUrl("upload"+File.separator+fileName); //File.separator 获取路径符号
//需求2: 存储多张图片路径
user.getUrList().add("upload"+File.separator+fileName);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
//将map的数据注入的User对象,["username":"zs","password":"123"]
try {
BeanUtils.populate(user, map);
request.setAttribute("urls", user.getUrList());
request.getRequestDispatcher("show2.jsp").forward(request, response);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
文件下载
主要实现方式有两种
1. 超链接下载
2. 以超链接的方式下载压缩文件
3. 以超链接的方式下载图片文件
4. 以超链接的方式下载中文文件名的文件
5. Servlet下载
有些超链接搞不定的事情就交给Servlet来搞定
采用流的方式来读和写
设置响应的头部信息
response.setHeader("Content-disposition", "attachment;fileName="+fileName);
细节:
下载的文件名是中文怎么办?
代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 英文路径直接下载 -->
<a href="download/b.jpg">下载b.jpg</a><br/>
<a href="download/b.rar">下载b.rar</a><br/>
<!-- 带中文路径 -->
<a href="DownloadServlet?path=download/高圆圆.rar">下载高圆圆.rar</a><br/>
</body>
</html>
package com.qf.servlet;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getParameter("path");
path = new String(path.getBytes("iso-8859-1"), "utf-8"); //download/高圆圆.jar
//注意: 往往上传或下载的图片名不要有中文,且不要以数字开头
//获取文件名
String fileName = path.substring(path.indexOf("/")+1);
//将中文文件名进行转码,类似cookie应用
fileName = URLEncoder.encode(fileName, "utf-8");
//设置响应的头部信息
response.setHeader("Content-disposition", "attachment;fileName="+fileName);
//注意:io流的读写,是针对完整路径
path = getServletContext().getRealPath(path);
//通过第三方方式写出去
IOUtils.copy(new FileInputStream(path), response.getOutputStream());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
总结:
1. 文件上传案例:
1. 导入jar包:Commons-fileupload和commons-io
2. 客户端的操作:
1.表单的请求方式:method=post
2.设置enctype=multipart/form-data
3.控件类型: type="file"
3. 服务器端的操作:
1.创建DiskFileItemFactory
2.创建ServletFileUpload
3.通过ServletFileUpload的parseRequest方法得到所有的FileItem
4. 遍历集合中的所有FileItem对象
判断如果为非文本表单内容,则将该图片通过io流方式取出
2. 文件上传操作及优化----版本升级
1. 获取到FileItem对象后,取出io流内容----先读后写
2. 存储图片路径跟着项目走(存到部署目录下);第三方io操作优化
3. 处理文件名重名问题: 加前缀uuid、加入时间目录
4. 存储完整表单数据: 文本表单、二进制数据等
建立实体类---存图片时,只需存图片路径即可
存文本表单时,需通过第三方jar包去注入
导入jar包: BeanUtils、commons-logging.jar
存复选框数据时,如何存?
判断map集合中是否存储该key,如果有则拼接;
5、存单张图片、存多张图片(实体类中要有集合的属性用于存url路径)
3. 文件的下载:
1.简单的图片下载可以通过超链接直接下载即可
2.如果超链接下载不了(例如中文图片名);通过servlet去处理
servlet处理步骤: 获取到图片路径
get请求的转码
提取图片的文件名用于设置头部信息,且仍需转码
通过io流进行下载: IOUtils.copy
other
写相对路径报错
然后
说明web项目和java项目还是有区别的
[站外图片上传中...(image-7b84af-1552350602309)]
.