文章目录
今天傍晚看了李宏利老师的PPT,感触很深,自己很早就实现了文件上传下载,可是一直没有机会梳理,看了他的PPT后,花了三小时写下此文。本文1-3参照老师的PPT,4-6贴出自己的项目。
1文件上传概述
1.1文件上传的作用
例如网盘就是用来上传下载文件的,填写一个完整的简历还需要上传照片。
1.2文件上传对页面的要求
上传文件的要求比较多,需要记一下:
- 必须使用表单,而不能是超链接;
- 表单的method必须是POST,而不能是GET;
- 表单的enctype必须是multipart/form-data;
- 在表单中添加file表单字段,即
<input type=”file”/>
例如:
<form action="${pageContext.request.contextPath}/FileUploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
文件1:<input type="file" name="file1"/><br/>
文件2:<input type="file" name="file2"/><br/>
<input type="submit" value="提交"/>
</form>
1.3 比对文件上传表单和普通文本表单的区别
通过 httpWatch
查看 文件上传表单
和 普通文本表单
的区别。
- 文件上传表单的
enctype="multipart/form-data"
,表示多部件表单数据; - 普通文本表单可以不设置enctype属性:
- 当
method="post"
时,enctype的默认值为application/x-www-form-urlencoded
,表示使用url编码正文; - 当
method="get"
时,enctype的默认值为null
,没有正文,所以就不需要enctype了。
- 当
1.4 对普通文本表单的测试
通过httpWatch测试,查看表单的请求数据正文,我们发现请求中只有文件名称,而没有文件内容。也就是说,当表单的 enctype不是multipart/form-data
时,请求中不包含文件内容,而只有文件的名称,这说明普通文本表单中 input:file
与 input:text
没什么区别了。
测试-前端页面
测试-请求正文
测试-请求头
1.5 文件上传对Servlet的要求
当提交的表单是文件上传表单时,那么对Servlet也是有要求的。
首先要肯定一点,文件上传表单的数据也是被封装到request对象中的。
request.getParameter(String)
方法获取指定的表单字段字符内容,但文件上传表单已经不在是字符内容,而是字节内容,所以失效。
这时可以使用request的getInputStream()方法获取ServletInputStream对象,它是InputStream的子类,这个ServletInputStream对象对应整个表单的正文部分(从第一个分隔线开始,到最后),这说明我们需要的解析流中的数据。当然解析它是很麻烦的一件事情,而Apache已经帮我们提供了解析它的工具:commons-fileupload。
可以尝试把request.getInputStream()这个流中的内容打印出来,再对比httpWatch中的请求数据。
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
InputStream in = request.getInputStream();
String s = IOUtils.toString(in);
System.out.println(s);
}
/*
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="username"
hello
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
aaa
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="file2"; filename="b.txt"
Content-Type: text/plain
bbb
-----------------------------7ddd3370ab2--
*/
2 commons-fileupload
为什么使用fileupload?
上传文件的要求比较多,需要记一下:
- 必须是
POST
表单;- 表单的enctype必须是
multipart/form-data
;- 在表单中添加file表单字段,即
<input type="file"/>
Servlet的要求:
- 不能再使用request.getParameter()来获取表单数据;
- 可以使用request.getInputStream()得到所有的表单数据,而不是一个表单项的数据;
这说明不使用fileupload,我们需要自己来对request.getInputStream()的内容进行解析。
2.1 fileupload概述
fileupload是由apache的commons组件提供的上传组件。它最主要的工作就是帮我们解析request.getInputStream()。
fileupload组件需要的JAR包有:
commons-fileupload.jar
,核心包;commons-io.jar
,依赖包。
下载地址:http://cdn.zxy97.com/s/20180930194932.zip
2.2 fileupload简单应用
fileupload的核心类有:DiskFileItemFactory、ServletFileUpload、FileItem。
使用fileupload组件的步骤如下:
- 创建工厂类DiskFileItemFactory对象:
- 使用工厂创建解析器对象:
- 使用解析器来解析request对象:
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
List<FileItem> list = fileUpload.parseRequest(request);
介绍FileItem
类,它才是我们最终要的结果。
一个FileItem对象对应一个表单项(表单字段)。一个表单中存在文件字段和普通字段,可以使用FileItem类的isFormField()方法来判断表单字段是否为普通字段,如果不是普通字段,那么就是文件字段了。
- String getName():获取文件字段的文件名称;
- String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
- String getFieldName():获取字段名称,例如:,返回的是username;
- String getContentType():获取上传的文件的类型,例如:text/plain。
- int getSize():获取上传文件的大小;
- boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
- InputStream getInputStream():获取上传文件对应的输入流;
- void write(File):把上传的文件保存到指定文件中。
2.3 简单上传示例
现在写一个简单的上传示例:
- 表单包含一个用户名字段,以及一个文件字段;
- Servlet保存上传的文件到uploads目录,显示用户名,文件名,文件大小,文件类型。
2.3.1 index.jsp
第一步:完成 index.jsp
,只需要一个表单。注意表单必须是post的,而且enctype必须是mulitpart/form-data的。
<form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
文件1:<input type="file" name="file1"/><br/>
<input type="submit" value="提交"/>
</form>
2.3.2 FileUploadServlet
第二步:完成FileUploadServlet
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 因为要使用response打印,所以设置其编码
response.setContentType("text/html;charset=utf-8");
// 创建工厂
DiskFileItemFactory dfif = new DiskFileItemFactory();
// 使用工厂创建解析器对象
ServletFileUpload fileUpload = new ServletFileUpload(dfif);
try {
// 使用解析器对象解析request,得到FileItem列表
List<FileItem> list = fileUpload.parseRequest(request);
// 遍历所有表单项
for(FileItem fileItem : list) {
// 如果当前表单项为普通表单项
if(fileItem.isFormField()) {
// 获取当前表单项的字段名称
String fieldName = fileItem.getFieldName();
// 如果当前表单项的字段名为username
if(fieldName.equals("username")) {
// 打印当前表单项的内容,即用户在username表单项中输入的内容
response.getWriter().print("用户名:" + fileItem.getString() + "<br/>");
}
} else {//如果当前表单项不是普通表单项,说明就是文件字段
String name = fileItem.getName();//获取上传文件的名称
// 如果上传的文件名称为空,即没有指定上传文件
if(name == null || name.isEmpty()) {
continue;
}
// 获取真实路径,对应${项目目录}/uploads,当然,这个目录必须存在
String savepath = this.getServletContext().getRealPath("/uploads");
// 通过uploads目录和文件名称来创建File对象
File file = new File(savepath, name);
// 把上传文件保存到指定位置
fileItem.write(file);
// 打印上传文件的名称
response.getWriter().print("上传文件名:" + name + "<br/>");
// 打印上传文件的大小
response.getWriter().print("上传文件大小:" + fileItem.getSize() + "<br/>");
// 打印上传文件的类型
response.getWriter().print("上传文件类型:" + fileItem.getContentType() + "<br/>");
}
}
} catch (Exception e) {
throw new ServletException(e);
}
}
3 文件上传之细节
3.1 把上传的文件放到WEB-INF目录下
如果没有把用户上传的文件存放到WEB-INF目录下,那么用户就可以可能通过浏览器直接访问上传的文件,这是非常危险的。
假如说用户上传了一个a.jsp文件,然后用户在通过浏览器去访问这个a.jsp文件,那么就会执行a.jsp中的内容,如果在a.jsp中有如下语句:Runtime.getRuntime().exec("shutdown –s –t 1");
那么你就会…
通常我们会在WEB-INF目录下创建一个uploads目录来存放上传的文件,而在Servlet中找到这个目录需要使用ServletContext的getRealPath(String)
方法。
例如在我的upload1项目中有如下语句:
ServletContext servletContext = this.getServletContext();
String savepath = servletContext.getRealPath("/WEB-INF/uploads");
/*
savepath为:F:\tomcat6_1\webapps\upload1\WEB-INF\uploads
*/
3.2 文件名称(完整路径、文件名称)
IE6获取的上传文件名称是完整路径,而其他浏览器获取的上传文件名称只是文件名称而已。浏览器差异的问题我们还是需要处理一下的。
String name = file1FileItem.getName();
response.getWriter().print(name);
使用不同浏览器测试,其中IE6就会返回上传文件的完整路径,不知道IE6在搞什么,这给我们带来了很大的麻烦,就是需要处理这一问题。
处理这一问题也很简单,无论是否为完整路径,我们都去截取最后一个“\”后面的内容就可以了。
String name = file1FileItem.getName();
int lastIndex = name.lastIndexOf("\\");//获取最后一个“\”的位置
if(lastIndex != -1) {//注意,如果不是完整路径,那么就不会有“\”的存在。
name = name.substring(lastIndex + 1);//获取文件名称
}
response.getWriter().print(name);
3.3 中文乱码问题
3.3.1 文件名称中包含中文时
需要设置编码,commons-fileupload组件为我们提供了两种设置编码的方式:
- request.setCharacterEncoding(String):这种方式是我们最为熟悉的方式了;
- fileUpload.setHeaderEncdoing(String):这种方式的优先级高于前一种。
3.3.2 文件内容中包含中文时
通常我们不需关心上传文件的内容,因为我们会把上传文件保存到硬盘上。
也就是说,文件原来是什么样子,到服务器这边还是什么样子,但是如果你有这样的需求,非要在控制台显示上传的文件内容,那么你可以使用fileItem.getString("utf-8")
来处理编码。
即:文本文件内容和普通表单项内容使用FileItem类的getString("utf-8")
来处理编码。
3.3 上传文件同名问题(文件重命名)
通常我们会把用户上传的文件保存到uploads目录下,但如果用户上传了同名文件呢?这会出现覆盖的现象。处理这一问题的手段是使用UUID生成唯一名称,然后再使用“_”连接文件上传的原始名称。
例如用户上传的文件是我的一寸照片.jpg
,在通过处理后,文件名称为:891b3881395f4175b969256a3f7b6e10_我的一寸照片.jpg
,这种手段不会使文件丢失扩展名,并且因为UUID
的唯一性,上传的文件同名,但在服务器端是不会出现同名问题的。
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
DiskFileItemFactory dfif = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(dfif);
try {
List<FileItem> list = fileUpload.parseRequest(request);
//获取第二个表单项,因为第一个表单项是username,第二个才是file表单项
FileItem fileItem = list.get(1);
String name = fileItem.getName();//获取文件名称
// 如果客户端使用的是IE6,那么需要从完整路径中获取文件名称
int lastIndex = name.lastIndexOf("\\");
if(lastIndex != -1) {
name = name.substring(lastIndex + 1);
}
// 获取上传文件的保存目录
String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");
String uuid = CommonUtils.uuid();//生成uuid
String filename = uuid + "_" + name;//新的文件名称为uuid + 下划线 + 原始名称
//创建file对象,下面会把上传文件保存到这个file指定的路径
//savepath,即上传文件的保存目录
//filename,文件名称
File file = new File(savepath, filename);
// 保存文件
fileItem.write(file);
} catch (Exception e) {
throw new ServletException(e);
}
}
3.5 一个目录不能存放过多的文件(存放目录打散)
一个目录下不应该存放过多的文件,一般一个目录存放1000个文件就是上限了,如果在多,那么打开目录时就会很“卡”。你可以尝试打印C:\WINDOWS\system32目录,你会感觉到的。
也就是说,我们需要把上传的文件放到不同的目录中。但是也不能为每个上传的文件一个目录,这种方式会导致目录过多。所以我们应该采用某种算法来“打散”!
打散的方法有很多,例如使用日期来打散,每天生成一个目录。也可以使用文件名的首字母来生成目录,相同首字母的文件放到同一目录下。
- 日期打散算法:如果某一天上传的文件过多,那么也会出现一个目录文件过多的情况;
- 首字母打散算法:如果文件名是中文的,因为中文过多,所以会导致目录过多的现象。
我们这里使用hash算法来打散:
获取文件名称的hashCode
:
获取hCode的低4位,然后转换成16进制字符;
获取hCode的5~8位,然后转换成16进制字符;
使用这两个16进制的字符生成目录链。例如低4位字符为5
//获取文件名的hashCode
int hCode = name.hashCode();
//获取hCode的低4位,并转换成16进制字符串
String dir1 = Integer.toHexString(hCode & 0xF);
//获取hCode的低5~8位,并转换成16进制字符串
String dir2 = Integer.toHexString(hCode >>> 4 & 0xF);
//与文件保存目录连接成完整路径
savepath = savepath + "/" + dir1 + "/" + dir2;
//因为这个路径可能不存在,所以创建成File对象,再创建目录链,确保目录在保存文件之前已经存在
new File(savepath).mkdirs();
3.7 上传的单个文件的大小限制
有时我们需要限制一个请求的大小。也就是说这个请求的最大字节数(所有表单项之和)!实现这一功能也很简单,只需要调用ServletFileUpload类的setSizeMax(long)方法即可。
例如fileUpload.setSizeMax(1024 * 10);
,则设置整个请求的上限为10KB。当请求大小超出10KB时,ServletFileUpload类的parseRequest()
方法会抛出FileUploadBase.SizeLimitExceededException
异常。
3.8 缓存大小与临时目录
大家想一想,如果我上传一个蓝光电影,先把电影保存到内存中,然后再通过内存copy到服务器硬盘上,那么你的内存能吃的消么?
所以fileupload组件不可能把文件都保存在内存中,fileupload会判断文件大小是否超出10KB,如果是那么就把文件保存到硬盘上,如果没有超出,那么就保存在内存中。
10KB是fileupload默认的值,我们可以来设置它。
当文件保存到硬盘时,fileupload是把文件保存到系统临时目录,当然你也可以去设置临时目录。
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
DiskFileItemFactory dfif = new DiskFileItemFactory(1024*20, new File("F:\\temp"));
ServletFileUpload fileUpload = new ServletFileUpload(dfif);
try {
List<FileItem> list = fileUpload.parseRequest(request);
FileItem fileItem = list.get(1);
String name = fileItem.getName();
String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");
// 保存文件
fileItem.write(path(savepath, name));
} catch (Exception e) {
throw new ServletException(e);
}
}
4 文件下载
文件下载的核心代码就三行:
response.addHeader("Content-Disposition", "attachment;filename=" + childFileName);
response.addHeader("Content-Length", childFileLength);
response.setContentType("application/octet-stream");
下面给出一个文件下载的方法实现:
/**
* @param filePath
* @param request
* @param response
* @return
* @throws java.io.FileNotFoundException
* @throws java.io.UnsupportedEncodingException
* @描述 文件下载
*/
public static boolean downloadFile(String filePath, HttpServletRequest request, HttpServletResponse response)
throws FileNotFoundException, UnsupportedEncodingException, IOException {
boolean isDownload = false;
File childFile = new File(filePath);
if (!childFile.exists()) {
System.out.println("文件不存在或者已删除-下载失败!" + childFile.getAbsolutePath());
} else {
try (InputStream fis = new FileInputStream(childFile); OutputStream os = response.getOutputStream()) {
String childFileName = URLEncoder.encode(childFile.getName(), "utf-8");
String childFileLength = childFile.length() + "";
response.addHeader("Content-Disposition", "attachment;filename=" + childFileName);
response.addHeader("Content-Length", childFileLength);
response.setContentType("application/octet-stream");
int data;
while ((data = fis.read()) != -1) {
os.write(data);
}
isDownload =true;
}
}
return isDownload;
}
5 实现一个图片外链网站
单就文件上传功能,现在你就可以来实现一个资源托管网站(http://cdn.zxy97.com)或者一个图片外链网站(http://img.zxy97.com),以下我将介绍实现图片外链网站的步骤。
5.1 项目结构
5.2 具体步骤
5.2.1 在项目中导入commons-fileupload-1.3.2.jar
和commons-io-2.5.jar
commons-fileupload-1.3.2.jar
和commons-io-2.5.jar
的下载地址:http://cdn.zxy97.com/s/20180930194932.zip
下载好后解压得到两个jar文件,在webroot/WEB-INF下创建lib
文件夹,复制进去。
然后再用IDE工具导入到项目中。
5.2.1 在src下创建com.zxy97.img.servlet
包
com.zxy97
是我的域名(zxy97.com)的反写,如果你想把项目直接跑起来,按照该步骤进行即可。
5.2.2 在该包下创建UploadServlet.java
package com.zxy97.img.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
static final String IMG_FOLDER = "img";//默认的存放img的根文件夹
/**
* @描述 上传数据及保存文件
* @param request
* @param response
* @return 上传结果
*/
public static String upload(HttpServletRequest request, HttpServletResponse response) {
try {
//设置上传的文件大小
final int MEMORY_THRESHOLD = 1024 * 1024 * 512;
final int MAX_FILE_SIZE = 1024 * 1024 * 1024;
final int MAX_REQUEST_SIZE = 1024 * 1024 * 1024;
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
if (!ServletFileUpload.isMultipartContent(request)) {
//检测是否为多媒体上传,如果不是则停止
PrintWriter writer = response.getWriter();
writer.println("错误:表单必须包含 enctype=multipart/form-data");
writer.flush();
return null;
}
DiskFileItemFactory factory = new DiskFileItemFactory();// 实例化,开始配置上传参数
factory.setSizeThreshold(MEMORY_THRESHOLD);// 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));// 设置临时存储目录
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(MAX_FILE_SIZE);// 设置最大文件上传值
upload.setSizeMax(MAX_REQUEST_SIZE);// 设置最大请求值 (包含文件和表单数据)
upload.setHeaderEncoding("UTF-8");// 防止中文乱码
// 解析请求的内容提取文件数据
List<FileItem> formItems = upload.parseRequest(request);
if (formItems != null && formItems.size() > 0) {
for (FileItem item : formItems) {
// 处理不在表单中的字段
if (!item.isFormField()) {
String itemName = item.getName();//获取文件名,进行验证
if(checkFileName(itemName)){//验证通过
java.util.Date date = new java.util.Date();//保存当时的系统时间,也可以用别的命名方式
String path = "/" + IMG_FOLDER + new java.text.SimpleDateFormat("/yyyyMMddHHmmss").format(date) + getFileSuffix(itemName);//相对路径
/*
推荐几种:
SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss/") 在img文件夹下创建多个文件夹保存文件
SimpleDateFormat("/yyyyMMddHHmmss_") 在img文件夹下将文件名改成这种形式
*/
String filePath = request.getSession().getServletContext().getRealPath("/") + path;
File file = new File(filePath);//要保存的文件
file.getParentFile().mkdirs();//为要保存的文件创建文件夹,否则无法保存
item.write(file);
return path;
}
}
}
}
return null;
}catch (Exception ex) {
return null;
}
}
/**
* @描述 获取文件后缀名
*/
private static String getFileSuffix(final String fileName){
int index = fileName.lastIndexOf(".");
if(index >= 0){
return fileName.substring(index);
}
return fileName;
}
// private String createFolder(String path){
// File folder = new File(path);
// if(!folder.exists()){
// if(folder.mkdirs()){
// return folder.getAbsolutePath();
// }
// }
// if(folder.isDirectory()){
// return folder.getAbsolutePath();
// }
// return null;
// }
/**
* @描述 允许上传的文件后缀
*/
private static final String []arrSuffix = {
".jpg",".png",".bmp",".ico",".jpge",".gif"
};
public static String arrSuffixToString(){
String str = "";
for(String s:arrSuffix){
str += s + " ";
}
return str;
}
/**
* @描述 根据允许上传的文件后缀,进行文件判断
*/
private static boolean checkFileName(final String fileName){
String name = fileName.toLowerCase();//转小写
for(String suffix:arrSuffix){
if(name.endsWith(suffix)){
return true;
}
}
return false;
}
/**
* @描述 去掉Servlet带来的冗余路径
*
*/
private static String getURL(String requestURL){
int index = requestURL.lastIndexOf("/");
return requestURL.substring(0, index);
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = upload(request, response);//上传
if(path == null){
path = "上传失败!支持的文件类型:" + arrSuffixToString() ;
}else{
path = getURL(request.getRequestURL().toString()) + path;
}
request.getSession().setAttribute("path", path);
response.sendRedirect(getURL(request.getRequestURL().toString()));
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Short description";
}
}
5.2.3 创建webroot/WEB-INF/web.xml
,用来配置servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.zxy97.img.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
5.2.4 创建webroot/index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%
String path =(String) session.getAttribute("path");
if(path == null){
path ="";
}
%>
<!DOCTYPE html>
<html>
<head>
<title>zxy97_免费图片托管</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h4><a href="http://zxy97.com/">Zxy97</a> | 图片托管网站</h4>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="uploadfile" title="请选择您的文件">
<button type="submit" class="btn">上传文件 </button>
</form>
<p><%=path %></p>
</body>
</html>
6 实现一个资源托管网站
如果你已经成功地将图片外链网站跑起来了,那么现在你可以对上面的那个项目稍加修改,动态地设置文件扩展名,用来实现一个资源托管网站:http://cdn.zxy97.com。
你在src下写的java代码会编译成class文件存放到webroot/WEB-INF/classes下,发布项目时只需要打包webroot文件夹,当时设置的文件扩展名便无法修改了,所以你可以将文件扩展名写到配置文件中,由程序读取,到时候只需要修改配置文件即可设置网站的文件上传类型、表单最大值、文件最大值、缓存最大值、保存的文件夹等等参数。
首先你需要在webroot/WEB-INF文件夹下创建一个名为config.properties的配置文件。
6.1 创建webroot/WEB-INF/config.properties
目前webroot/WEB-INF/config.properties的内容只有一行,就是用空格分隔的文件扩展名:
suffixArray=.jpg .png .bmp .ico .jpge .gif .zip .rar .7z .mp3 .txt .html .xml
6.2 创建com/zxy97/img/GetPath.java
package com.zxy97.img.servlet;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
public class GetPath {
public String[] getSuffixArray() throws IOException{
String str = getValueByKey("suffixArray");
return str.split(" ");
}
private String getValueByKey(String key) throws IOException{
return getValueByKey(getWebRootPath()+"/WEB-INF/config.properties",key);
}
private String getValueByKey(String configFilePath, String key) throws FileNotFoundException, IOException{
String value;
Properties prop = new Properties();
InputStream in = new BufferedInputStream (new FileInputStream(configFilePath));
prop.load(in);
value = prop.getProperty(key);
return value;
}
/**
* @return WebRoot的绝对路径,在src和jsp中通用的方法
*/
public String getWebRootPath() {
final String strClassName = getClass().getName();
String strPackageName = "";
if (getClass().getPackage() != null) {
strPackageName = getClass().getPackage().getName();
}
String strClassFileName;
if ("".equals(strPackageName)) {
strClassFileName = strClassName;
} else {
strClassFileName = strClassName.substring(strPackageName.length() + 1, strClassName.length());
}
URL url = getClass().getResource(strClassFileName + ".class");
String strURL = url.toString();
strURL = strURL.substring(strURL.indexOf("/") + 1, strURL.lastIndexOf("WEB-INF"));
return strURL;
}
}
6.3 创建com.zxy97.img/UploadServlet.java
package com.zxy97.img.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
static final String IMG_FOLDER = "s";//默认的存放文件的根文件夹
/**
* @描述 上传数据及保存文件
* @param request
* @param response
* @return 上传结果
*/
public static String upload(HttpServletRequest request, HttpServletResponse response) {
try {
//设置上传的文件大小
final int MEMORY_THRESHOLD = 1024 * 1024 * 512;
final int MAX_FILE_SIZE = 1024 * 1024 * 1024;
final int MAX_REQUEST_SIZE = 1024 * 1024 * 1024;
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
if (!ServletFileUpload.isMultipartContent(request)) {
//检测是否为多媒体上传,如果不是则停止
PrintWriter writer = response.getWriter();
writer.println("错误:表单必须包含 enctype=multipart/form-data");
writer.flush();
return null;
}
DiskFileItemFactory factory = new DiskFileItemFactory();// 实例化,开始配置上传参数
factory.setSizeThreshold(MEMORY_THRESHOLD);// 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));// 设置临时存储目录
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(MAX_FILE_SIZE);// 设置最大文件上传值
upload.setSizeMax(MAX_REQUEST_SIZE);// 设置最大请求值 (包含文件和表单数据)
upload.setHeaderEncoding("UTF-8");// 防止中文乱码
// 解析请求的内容提取文件数据
List<FileItem> formItems = upload.parseRequest(request);
if (formItems != null && formItems.size() > 0) {
for (FileItem item : formItems) {
// 处理不在表单中的字段
if (!item.isFormField()) {
String itemName = item.getName();//获取文件名,进行验证
if(checkFileName(itemName)){//验证通过
java.util.Date date = new java.util.Date();//保存当时的系统时间,也可以用别的命名方式
String path = "/" + IMG_FOLDER + new java.text.SimpleDateFormat("/yyyyMMddHHmmss").format(date) + getFileSuffix(itemName);//相对路径
/*
推荐几种:
SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss/") 在img文件夹下创建多个文件夹保存文件
SimpleDateFormat("/yyyyMMddHHmmss_") 在img文件夹下将文件名改成这种形式
*/
String filePath = request.getSession().getServletContext().getRealPath("/") + path;
File file = new File(filePath);//要保存的文件
file.getParentFile().mkdirs();//为要保存的文件创建文件夹,否则无法保存
item.write(file);
return path;
}
}
}
}
return null;
}catch (Exception ex) {
return null;
}
}
/**
* @描述 获取文件后缀名
*/
private static String getFileSuffix(final String fileName){
int index = fileName.lastIndexOf(".");
if(index >= 0){
return fileName.substring(index);
}
return fileName;
}
// private String createFolder(String path){
// File folder = new File(path);
// if(!folder.exists()){
// if(folder.mkdirs()){
// return folder.getAbsolutePath();
// }
// }
// if(folder.isDirectory()){
// return folder.getAbsolutePath();
// }
// return null;
// }
/**
* @描述 允许上传的文件后缀
*/
private static String []arrSuffix;
public static String arrSuffixToString(String []strs){
String str = "";
for(String s:strs){
str += s + " ";
}
return str;
}
/**
* @描述 根据允许上传的文件后缀,进行文件判断
*/
private static boolean checkFileName(final String fileName) throws IOException{
String name = fileName.toLowerCase();//转小写
arrSuffix = new GetPath().getSuffixArray();
for(String suffix:arrSuffix){
if(name.endsWith(suffix)){
return true;
}
}
return false;
}
/**
* @描述 去掉Servlet带来的冗余路径
*
*/
private static String getURL(String requestURL){
int index = requestURL.lastIndexOf("/");
return requestURL.substring(0, index);
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = upload(request, response);//上传
if(path == null){
path = "上传失败!支持的文件类型:" + arrSuffixToString(arrSuffix) ;
}else{
path = getURL(request.getRequestURL().toString()) + path;
}
request.getSession().setAttribute("path", path);
response.sendRedirect(getURL(request.getRequestURL().toString()));
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Short description";
}
}
6.4 创建webroot/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.zxy97.img.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
6.5 创建webroot/index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%
String path =(String) session.getAttribute("path");
if(path == null){
path ="";
}
%>
<!DOCTYPE html>
<html>
<head>
<title>zxy97_免费资源托管</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h4><a href="http://zxy97.com/">Zxy97</a> | 资源托管网站</h4>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="uploadfile" title="请选择您的文件">
<button type="submit" class="btn">上传文件 </button>
</form>
<p><%=path %></p>
</body>
</html>