Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布
1、文件的上传介绍(*****重要)
- 文件的上传主要分成两个步骤
- 在含有<input type=”file” >的表单页面中选择文件,然后将请求提交到Servlet
- Servlet收到请求,解析用户上传的文件,然后将文件存储到服务器
文件上传的时候我们需要使用 <input type="file" /> 这样的一个表单项。
并且还必须修改<form method="post" enctype="multipart/form-data">
文件上传的时候,method必须为post,并且enctype必须为multipart/form-data
当enctype=application/x-www-form-urlencoded 的时候,表示,浏览器要把所有的表单项,以name=value&name=value的形式进行拼接。
然后做url的编码操作,再发送到服务器
当enctype=multipart/form-data 的时候,表示浏览器会把表单提交的数据,以二进制流的形式提交到服务器。
并且会以多段的形式来组装二进制流数据。
1.1、浏览器端的编解码操作。
<script type="text/javascript">
var url = "username=中文&password=123456";
// 这叫编码操作。由浏览器完成!!!
var encodedUrl = encodeURI(url);
alert(encodedUrl);
// 把编码后的url解码操作,还原成原来的地址。
var decodeURL = decodeURI(encodedUrl);
alert(decodeURL);
</script>
1.2、Java中的编解码操作。
public static void main(String[] args) throws UnsupportedEncodingException {
String url = "username=中文&password=123456";
// 把地址进行编码操作
String encodeURL = URLEncoder.encode(url, "UTF-8");
System.out.println(encodeURL);
// 把地址进行解码操作。
String decodeURL = URLDecoder.decode(encodeURL, "UTF-8");
System.out.println(decodeURL);
}
1.3、文件上传,HTTP协议的说明。
要解析上面协议的内容。我们自己解析是很困难的。那我们怎么办呢。我们需要使用Apache组织给我们提供的一个叫做commons-fileupload-1.2.1.jar的jar包来解析。fileupload包需要依赖一个commons-io-1.4.jar。所以我们只需要使用这两个jar包就可以解析得到我们想要的内容。具体如何使用,请看下面的java代码。 下载地址:https://download.****.net/download/qq_25106373/10577746
1.4、commons-fileupload.jar 常用API介绍说明
boolean ServletFileUpload.isMultipartContent(HttpServletRequest request);
判断请求是否是文件上传的 multipart/form-data 格式
public List<FileItem> parseRequest(HttpServletRequest request)
解析request对象,获取请求参数,返回的是一个List,List中保存的是一个FileItem对象,一个对象代表一个请求表单项。
boolean FileItem.isFormField()
判断当前表单项是否是普通表单项。如果是普通项。返回true,如果是文件上传项。返回false
String FileItem.getFieldName()
返回表单项的name属性值
String FileItem.getString()
返回表单项的值。
String FileItem.getName();
返回上传文件的文件名。
void FileItem.write( file );
把上传的文件输入到指定的file位置
FileItemFactory 我们使用的是DiskFileItemFactory
解析Request对象使用ServletFileUpload类实例的.parseRequest(HttpServletRequest request)方法
1.5、fileupload类库的使用:
// 1、先判断是否是上传的协议
// 2、创建一个文件工厂FileItemFactory
// 3、创建一个用来解析上传协议的类ServletFileUpload
// 4、解析request的HTTP协议数据,生成表单项 parseRequest(HttpServletRequest request)
// 5、遍历处理每一个表单项FileItem。判断每个表单项。是否为上传文件。而做不同的相应的处理。
2)html页面中文件上传的表单内容:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
表单上传:
<form action="upLoad" method="post" enctype="multipart/form-data">
用户名:<input name="username" type="text" /><br/>
头像上传:<input name="file" type="file" /><br/>
<input type="submit" />
</form>
</body>
</html>
3)创建一个Servlet类名为:UpLoad来接收上传的内容。代码如下:
fileupload类库的使用步骤:
// 1、先判断是否是上传的协议
// 2、创建一个文件工厂FileItemFactory
// 3、创建一个用来解析上传协议的类ServletFileUpload
// 4、解析request的HTTP协议数据,生成表单项
// 5、遍历处理每一个表单项。判断每个表单项。是否为上传文件。而做不同的相应的处理。
package com.atguigu.servlet;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
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;
public class UpLoad extends HttpServlet {
private static final long serialVersionUID = 1L;
public UpdateLoad() {
super();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1、先判断是否是上传的协议
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
try {
// 2、创建一个文件工厂FileItemFactory
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 3、创建一个用来解析上传协议的类ServletFileUpload
ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);
// 4、解析request数据
List<FileItem> fileItems = fileUpload.parseRequest(request);
System.out.println("表单项个数:" + fileItems.size());
// 5、遍历处理每一个表单项
for (FileItem fileItem : fileItems) {
// 判断是否是普通的表单项,还是上传的文件项
if (fileItem.isFormField()) {
System.out.println(fileItem.isFormField());
// 获取表单项name的值
System.out.println(fileItem.getFieldName());
// 获取表单项值
System.out.println(fileItem.getString());
} else {
// 打印测试
System.out.println(fileItem.isFormField());
System.out.println(fileItem.getFieldName());
// 获取文件名
System.out.println(fileItem.getName());
//生成uuid名字
String uuid = UUID.randomUUID().toString().replaceAll("-","");
System.out.println(uuid);
// 保存到工程中upload目录下
File f = new File(getServletContext().getRealPath("/upload") + "/" + uuid + "_"
+ fileItem.getName());
System.out.println("file 路径:" + f);
// 把文件写入到指定路径。
fileItem.write(f);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("不是合法的文件上传!!");
}
}
}
3)在web.xml中的配置:
<servlet>
<servlet-name>UpLoad</servlet-name>
<servlet-class>com.atguigu.servlet.UpLoad</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UpLoad</servlet-name>
<url-pattern>/upLoad</url-pattern>
</servlet-mapping>
2、文件上传练习
需求:在前面图像上传功能的基础之上。上传成功之后。跳转到一个页面把所有上传的图片都展示出来。
1) 修改上面的UpdateLoad类,实现显示功能
package com.atguigu.servlet;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
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;
public class UpLoad extends HttpServlet {
private static final long serialVersionUID = 1L;
// 使用map结构代码数据库保存
private Map<String, String> imgs = new ConcurrentHashMap<String, String>();
public UpdateLoad() {
super();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 用户名
String username = null;
// 头像路径
String imgPath = null;
// 1、先判断是否是上传的协议
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
try {
// 2、创建一个文件工厂FileItemFactory
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 3、创建一个用来解析上传协议的类ServletFileUpload
ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);
// 4、解析request数据
List<FileItem> fileItems = fileUpload.parseRequest(request);
System.out.println("表单项个数:" + fileItems.size());
// 5、遍历处理每一个表单项
for (FileItem fileItem : fileItems) {
// 判断是否是普通的表单项,还是上传的文件项
if (fileItem.isFormField()) {
System.out.println(fileItem.isFormField());
// 获取表单项name的值
System.out.println(fileItem.getFieldName());
// 获取表单项值
username = fileItem.getString("UTF-8");
System.out.println(fileItem.getString("UTF-8"));
} else {
// 打印测试
System.out.println(fileItem.isFormField());
System.out.println(fileItem.getFieldName());
// 获取文件名
System.out.println(fileItem.getName());
// 生成uuid名字
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
imgPath = request.getContextPath() + "/upload/" + uuid + "_"
+ fileItem.getName();
System.out.println(imgPath);
// 保存到工程中upload目录下
File f = new File(getServletContext().getRealPath("/upload") + "/" + uuid
+ "_" + fileItem.getName());
System.out.println("file 路径:" + f);
// 把文件写入到指定路径。
fileItem.write(f);
}
}
// 添加到map中
imgs.put(username, imgPath);
// 存放到request域对象中
request.setAttribute("imgs", imgs);
// 转发到展示页面
request.getRequestDispatcher("showImgs.jsp").forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("不是合法的文件上传!!");
}
}
}
2) 添加一个showImg.jsp页面显示所有上传的图片
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%-- 遍历图片 --%>
<c:forEach items="${ imgs }" var="item">
<div style="width: 120px; float: left; border: 1px red solid; padding: 5px;">
<span>${ item.key }</span>
<img width="100" height="120" src="${ item.value }" />
</div>
</c:forEach>
</body>
</html>
3、文件下载
- 文件的下载需要把文件以二进制流的形式返回给客户端。并且告诉客户端返回的数据的类型。
获取客户端的输出流:response.getOutputStream();
获取下载文件的输入流:servletContext.getResourceAsStream();
设置响应的数据类型 response.setContentType();
获取文件的数据类型 servletContext.getMimeType();
1、下载的步骤:
- 读取下载的文件,获取到输入流对象
- 通过response对象获取到输出流
- 把下载的数据,通过输出流。输出到客户端。
- 输出之前我们要告诉浏览器输出的数据的类型response.setContentType();
2、但是通过前面的操作,我们不难发现。有些像图片,文本的数据,浏览器会直接显示出来。而我们的目的是想让浏览器进行下载操作。
所以这个时候需要设置响应头
response.setHeader("Content-Disposition", "attachment; fileName=1.jpg");
这个响应头告诉浏览器。这是需要下载的。而attachment表示附件,也就是下载的一个文件。fileName=后面,表示下载的文件名。
3、完成上面的两个步骤,下载文件是没问题了。但是如果我们要下载的文件是中文名的话。你会发现,下载无法正确显示出正确的中文名。
原因是在响应头中,不能包含有中文字符,只能包含ASCII码。
附件中文名乱码问题解决方案:
方案一:URLEncoder解决 IE 和谷歌浏览器的 附件中文名问题。
如果客户端浏览器是IE浏览器 或者 是谷歌浏览器。我们需要使用URLEncoder类先对中文名进行UTF-8的编码操作。
因为IE浏览器和谷歌浏览器收到含有编码后的字符串后会以UTF-8字符集进行解码显示。
// 把中文名进行UTF-8编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);
BASE64编解码的说明
如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行BASE64的编码操作。
这时候需要把请求头Content-Disposition: attachment; filename=中文名
编码成为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=
=?charset?B?xxxxx?= 现在我们对这段内容进行一下说明。
=? 表示这段编码内容开始的符号
charset 表示编码的字符集
B 表示使用Base64进行编码操作。
xxxx 表示 filename原来的值进行Base64编码后的内容。
?= 表示整段编码内容的结束标记。
BASE64编解码使用示例:
public static void main(String[] args) throws Exception {
String string = "中文";
// 生成BASE64的编码器
BASE64Encoder encoder = new BASE64Encoder();
// 编码操作
String encodeString = encoder.encode(string.getBytes("UTF-8"));
System.out.println(encodeString);
// 生成一个BASE64的解码器
BASE64Decoder decoder = new BASE64Decoder();
// 解码为byte数组
byte[] buffer = decoder.decodeBuffer(encodeString);
// 将字节数组还原为原来的中文
String string2 = new String(buffer, "UTF-8");
System.out.println(string2);
}
方案二:BASE64编解码 解决 火狐浏览器的附件中文名问题
因为火狐使用的是BASE64的编解码方式还原响应中的汉字。所以需要使用BASE64Encoder类进行编码操作。
// 使用下面的格式进行BASE64编码后
String str = "attachment; fileName=" + "=?utf-8?B?"
+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);
那么我们如何解决上面两种不同编解码方式呢。我们只需要通过判断请求头中User-Agent这个请求头携带过来的浏览器信息即可判断出是什么浏览器。
如下:
String ua = request.getHeader("User-Agent");
// 判断是否是火狐浏览器
if (ua.contains("Firefox")) {
// 使用下面的格式进行BASE64编码后
String str = "attachment; fileName=" + "=?utf-8?B?"
+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);
} else {
// 把中文名进行UTF-8编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);
}
方案三:使用 GBK编码,ISO-8859-1解码方式解决 附件中文名问题(非标准方案)
不管是任何的浏览器。只需要把中文名通过gbk字符集方式进行编码。然后再以iso-8859-1字符集进行解码。然后于设置到响应头中。经实验。可以在任何浏览器中成功解决中文名的问题。
// 解决下载文件名中文乱码,
// 此方法无解,但是管用
String str = "attachment; fileName=" + new String("中文.jpg".getBytes("gbk"), "iso-8859-1");
response.setHeader("Content-Disposition", str);
package com.atguigu.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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;
import sun.misc.BASE64Encoder;
/**
* Servlet implementation class Download
*/
public class Download extends HttpServlet {
private static final long serialVersionUID = 1L;
public Download() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 读取要下载的文件
InputStream is = getServletContext().getResourceAsStream("/upload/2.jpg");
// 获取输出流
OutputStream os = response.getOutputStream();
// 获取文件类型// 这里请换成你自己的文件名去下载
String mimeType = getServletContext().getMimeType("/upload/2.jpg");
// 告诉浏览器。返回的内容类型。为下载
response.setContentType(mimeType);
// 解决下载文件名中文乱码,方法一
// 通过判断不同浏览器,做不同的处理
String ua = request.getHeader("User-Agent");
if (ua.contains("Firefox")) {
String str = "attachment; fileName=" + "=?utf-8?B?"
+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
response.setHeader("Content-Disposition", str);
} else {
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
response.setHeader("Content-Disposition", str);
}
// 解决下载文件名中文乱码,方法二
// 此方法无解,但是管用
// String str = "attachment; fileName=" + new String("中文.jpg".getBytes("gbk"), "iso-8859-1");
// response.setHeader("Content-Disposition", str);
// 从输入流读取数据,输出到输出流
IOUtils.copy(is, os);
is.close();
os.close();
}
}
4、打包布置
首先,我们大多数时候布署的服务器,通常是linux系统。这个我们之后再谈。今天我们来看看。如何在window下布署工程。
前面我们讲过。在Tomcat中布署工程,只需要把工程布署到webapps目录下即可。那我们的web工程。发布署之前。还要做一件事情,那就是打包为war文件。
4.1、如何将web工程打包成为war文件。
1)选中你要发布的web工程。右键,选Expor菜单 。t
2)过滤找到我们要导出的war。点击Next>下一步
3)选择导出的配置
最后得到的是这样的一个文件:
然后我们可以把上面这个book.war文件拷贝到webapps目录下,启动Tomcat即可访问book工程。
4.2、将Tomcat 安装成window服务
我们如果要使用window系统来做服务器。那么Tomcat服务器就必须要开机启动。安装成为window服务进程。安装的方法如上图。
1)先cd 到Tomcat的bin目录下
2)然后执行service.bat install 命令
3)然后到服务管理界面,我们发现。Apache Tomcat 6 服务器 已经被安装到系统中
4)移除安装的服务 执行service.bat remove 命令。