《JavaWeb从入门到改行》fileupload,没毛病
目录:
> 文件上传的要求
fileupload API
文件上传的要求
上传文件对页面的要求:
- 必须使用表单,不能用超链接
- 表单的method必须是post, 不能是GET
- 表单的enctype必须是multipart/form-data
- 表单字段<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>
上传文件对Servlet的要求:
- 当一个表单中存在普通表单项字段和文件表单项字段时,就不能使用request.getParameter(String) 来获取了,因为request.getParameter(String) 获取的是字符内容,而上传的文件是字节内容,所以要使用request.getInputStream()来得到ServletInputStream对象,(InputStream的子类)。 ServletInputStream对象对应所有表单数据(包含文件项和普通项)。 所以当有文件作为表单项时,我们需要操作的是ServletInputStream对象
fileupload组件
ServletInputStream对象是个流对象,我们需要去解析流中的数据,过于麻烦 。所以利用apache提供的Commons FileUpload 组件,这个组件能帮助我们方便的解析 。
Commons FileUpload 组件 包含 两个包: commons-fileupload.jar(核心包) 、 commons-io.jar (依赖包),如果使用组件,则必须导入这个两个包 。 (两个包的下载,在本文项目案例下载中)
fileupload的核心类有 : DiskFileItemFactory(工厂) 、ServletFileUpload(解析器) 、 FileItem(表单项)
fileupload组件的使用步骤如下:
- 创建工厂类DiskFileItemFactory 对象 : DiskFileItemFactory factory = new DiskFileItemFactory()
- 使用工厂创建解析器对象 : ServletFileUpload fileUpload = new ServletFileUpload(factory)
- 使用解析器来解析request对象:List<FileItem> list = fileUpload.parseRequest(request)
FileItem类是我们要的结果,一个FileItem对象对应一个表单项(一个表单字段),一个表单中存在文件字段和普通字段 , 该类的一些方法如下:
String getName() | 获取文件字段的文件名称 |
String getString(String charset) | 返回表单项的值 |
boolean isFormField() | 是否为普通表单项!返回true为普通表单项 |
String getFieldName() | 返回当前表单项的名称 |
long getSize() | 返回上传文件的字节数 |
InputStream getInputStream() | 返回上传文件对应的输入流 |
void write(File destFile) | 把上传的文件内容保存到指定的文件中 |
String getContentType() | 获取MIME类型 |
上传细节的代码演示
代码中 包含的内容 : 上传表单项与普通表单项 + 目录打散 + 缓存 + 文件大小限制
运行代码前: 应现在项目的WEB-INF目录下创建files目录 。 在D盘下创建temp目录
<h1>上传Demo1</h1>
<h3>${msg }</h3>
<form action="<c:url value='/UploadServlet'/>" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
照 片:<input type="file" name="zhaopian"/><br/>
<input type="submit" value="上传"/>
</form>
package cn.kmust.web.demo1.UpLoadServlet; import java.io.File;
import java.io.IOException;
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.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload; import cn.itcast.commons.CommonUtils;
/**
* 处理上传的图片
* @功能 保存到web-inf下的files文件中,目录自动生成
* 有缓存的设置
* 有文件大小限制设置
* 目录打散 : 能够自动保存到web-inf下的files文件中创建目录,保存文件
* 能够保存到本地硬盘中
* @author ZHAOYUQIANG
*
*/
public class UploadServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
/*
* 1. 得到工厂
* 设置缓存大小和临时目录: 上传的时候是存到内存的,如果过大,内存会吃不消,
* 需要先在硬盘中(D:/temp)存一下,每20*1024就往硬盘中存一下
*
* 2. 通过工厂创建解析器
* 对大小进行限制 ,放在解析之前
* 如果超过大小会抛出异常,抛出异常的类型不同
*/
DiskFileItemFactory factory = new DiskFileItemFactory(50*1024,new File("D:/temp"));
ServletFileUpload sfu = new ServletFileUpload(factory);
/**
* 功能1. 对单个文件大小和整个表单大小进行限制
* 并且在后面的异常中进行提示
*/
//限制单个文件大小为50KB,抛出FileUploadBase.FileSizeLimitExceededException
sfu.setFileSizeMax(50*1024);//并且限制单个文件大小为5M,抛出文件大小限制异常
//限制整个表单大小为50KB,抛出FileUploadBase.SizeLimitExceededException
//sfu.setSizeMax(50*1024);
try {
/*
* 3. 解析request,得到FileItem集合
* 4. 遍历FileItem集合,得到普通表单项(用户名等)和
* 文件表单项(上传的文件)
*/
List<FileItem> fileItemList = sfu.parseRequest(request);
FileItem fi1 = fileItemList.get(0); //得到第一个表单项
FileItem fi2 = fileItemList.get(1); //得到第二个表单项
//////////////////////////演示普通表单项与文件表单项/////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/*
* 5. 输出普通表单项的演示
* 输出普通表单项的名和值
*
*/
System.out.println("@@@@@@@@@");
System.out.println("普通表单项演示 : "+fi1.getFieldName()+
"="+fi1.getString("UTF-8"));
/*
* 6. 输出文件表单项的相关演示
* 对于文件表单项:获取MIME类型、获取大小、获取文件名字
*/
System.out.println("文件表单项演示 :");
System.out.println("Content-Type :"+fi2.getContentType());
System.out.println("size :"+fi2.getSize());
System.out.println("filename :"+fi2.getName()); ////////////////////////////保存文件到web-inf下的files目录////////////////////////
//////////////////////////////////////////////////////////////////////////////
/**
* 1. 保存文件 [目录打散]
* 保存到web-inf下的files,这样安全
* 自动保存,文件目录自动创建,按照哈希打散生成两级目录,
* 分别取的十六进制前两个字符当作目录的一级和两级
*/
/*
* 1.1. 得到文件保存根路径
* 1.2. 得到文件名称
* 1.3. 进行两步处理
* 1.4. 用文件名得到整形的hashCode,把hashCode转换成十六进制
* 1.5. 获取十六进制前两个字符与根路径连接起来生成完成的目录路径
* 1.6. 路径有了之后,根据路径在磁盘上创建目录链
* 如果有了该目录什么也不做,如果没有就创建
* 1.7. 保存文件
*/
String root = this.getServletContext().getRealPath("/WEB-INF/files/");
String filename = fi2.getName();
/**
* 解决可能遇到的问题1 :
* 有的浏览器得到的文件名是绝对路径c:\XX\a.jpg,我们只是需要后截的路径
* 如果得到的是绝对路径,需要截取后半段的路径
*/
int index = filename.lastIndexOf("\\"); //转义
if(index != -1){ //说明有\,说明是绝对路径
filename = filename.substring(index+1);//截取出后半段的路径
}
/**
* 解决可能遇到的问题2 :
* 文件目录是自动生成的,可能存在文件同名问题
* 在生成的文件目录前面加上不同的id(利用commons中的uuid来生成)
*/
String savename = CommonUtils.uuid()+"_"+filename;
int hCode = filename.hashCode();
String hex = Integer.toHexString(hCode);
File dirFile = new File(root,hex.charAt(0)+"/"+hex.charAt(1)); dirFile.mkdirs();
File destFile = new File(dirFile,savename);
try {
fi2.write(destFile);
} catch (Exception e) {
throw new RuntimeException(e);
}
} catch (Exception e) {
/*
* 处理单个文件大小限制异常
*/
if(e instanceof FileUploadBase.FileSizeLimitExceededException){
request.setAttribute("msg", "您上传的文件超出了50KB!");
request.getRequestDispatcher("/formDemo1.jsp").forward(request, response);
}
throw new ServletException(e);
}
}
}
注意 : 文件大小对目录的限制,如果超过规定的大小太多,则直接报异常错误,页面也显示无法访问此网站等信息。这是因为common-fileupload组件默认最大支持上传文件的大小为2M,当我们上传大于2M的文件时,这个异常的发生导致了fileUpload拦截器没有机会执行 。
上传的文件要到tomcat下项目的相关目录中查看
项目案例---上传头像并显示(MVC+MySQL)
CREATE TABLE `tb_employe` (
`eid` char(32) NOT NULL,
`ename` char(100) NOT NULL,
`gender` varchar(10) DEFAULT NULL,
`image` varchar(200) DEFAULT NULL,
PRIMARY KEY (`eid`)
)
重要代码演示: (全部代码请下载项目查看,注意修改c3p0文件的数据库名称)
<h1><center>添加员工</center></h1>
<form action="<c:url value='/EmployeAddServlet'/>" method="post" enctype="multipart/form-data">
编号 :<input type="text" name="eid" /><br/>
姓名: <input type="text" name ="ename"/><br/>
性别: <input type="radio" name="gender" value="男" />男
<input type="radio" name="gender" value="女"/>女 <br>
头像: <input type="file" name="image" /> <br/>
<input type="submit" value="添加"/>
</form>
<c:forEach items="${employeList }" var="employe">
<div class="icon">
<img src="<c:url value='/${employe.image }'/>" border="0"/><br/>
<center>
<table border="0">
<tr>
<td>编号: </td><td>${employe.eid }</td>
</tr>
<tr>
<td>姓名: </td><td>${employe.ename }</td>
</tr>
<tr>
<td>性别: </td><td>${employe.gender }</td>
</tr>
</table>
</center>
</div>
</c:forEach>
package cn.kmust.employe.web.servlet; import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.ImageIcon; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload; import cn.itcast.commons.CommonUtils;
import cn.kmust.employe.domain.Employe;
import cn.kmust.employe.service.EmployeService; /**
* web层
* @author ZHAOYUQIANG
*
*/
public class EmployeAddServlet extends HttpServlet {
private EmployeService employeService = new EmployeService(); public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8"); /*
* 上传三步
* 创建工厂、得到解析器、使用解析器去解析request对象,得到List<FileItem>
*/
DiskFileItemFactory factory = new DiskFileItemFactory(100*1024,new File("D:/temp"));
ServletFileUpload sfu = new ServletFileUpload(factory);
/*
* 校验 头像是否>50KB
*/
//sfu.setFileSizeMax(50*1024);
try{
List<FileItem> fileItemList = sfu.parseRequest(request);
/*
* 把fileItemList中的数据封装到Employe对象中
*/
Map<String,String> map = new HashMap<String,String>();
/*
* 首先把所有的普通表单字段先封装到Map中,再把map中的数据封装到Employe中
*/
for(FileItem fileItem : fileItemList){
if(fileItem.isFormField()){ //判断是不是普通表单项
map.put(fileItem.getFieldName(), fileItem.getString("UTF-8"));
}
}
Employe employe = CommonUtils.toBean(map, Employe.class);
/*
* 保存上传的文件,即把上传的头像图片保存到带有盘符的目录中
* 要保存的路径
* 要保存的文件名称
*/
String savepath = this.getServletContext().getRealPath("/images");//得到保存目录
//得到文件名称,给文件名称添加uuid前缀,避免名称冲突。
//需要注意: get(3)表示文件表单项在表单的第3个位置3
String filename = CommonUtils.uuid()+"_"+fileItemList.get(3).getName();
// for(int i =0;i<30;i++){
// System.out.println(i+":"+fileItemList.get(i).getName());
// }
/*
* 校验文件扩展名是否是jpg格式的
*/
if(!filename.toLowerCase().endsWith("jpg")){
request.setAttribute("msg", "您上传的图片不是jpg扩展名!");
request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response);
return ;
}
File destFile = new File(savepath,filename); //使用目录和文件名创建目标文件
fileItemList.get(3).write(destFile);//保存上传文件到目标位置
/*
* 设置Employe对象的image
*/
employe.setImage("images/"+filename);
/*
* 调用service完成添加
*/
System.out.println("3333333333333333333");
employeService.add(employe);
/*
* 校验图片的尺寸
*/
Image image = new ImageIcon(destFile.getAbsolutePath()).getImage();
if(image.getWidth(null)>200 || image.getHeight(null)>200){
//删除图片
destFile.delete();
request.setAttribute("msg", "您上传的图片尺寸超过了200*200");
request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response);
return ;
}
/*
* 完成添加,返回员工列表
*/
request.getRequestDispatcher("/EmployeServlet?method=findAll").forward(request, response);
}catch(Exception e){
/*
* 处理上传图片过程中出现的图片大小异常
*/
if(e instanceof FileUploadBase.FileSizeLimitExceededException){
request.setAttribute("msg", "您上传的图片超过了50KB");
request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response);
}
}
}
}
项目下载 : https://files.cnblogs.com/files/zyuqiang/fileuploadProject.zip
《JavaWeb从入门到改行》fileupload,没毛病的更多相关文章
- 《JavaWeb从入门到改行》过滤器学习笔记
>"; display: block; height: 0; clear: both; visibility: hidden; } #sitemap, #sitemap ul{disp ...
- 《JavaWeb从入门到改行》JSP+EL+JSTL大杂烩汤
title: Servlet之JSP tags: [] notebook: javaWEB --- JSP是什么 ? JSP就是Servlet,全名是"JavaServer Pages&qu ...
- 《JavaWeb从入门到改行》JDBC经典秘方QueryRunner
目录: 基础篇_功能各自回顾 JDBC基础代码回顾(使用JdbcUtils工具简化) c3p0数据库连接池的使用(使用JdbcUtils工具简化) 大数据的插入(使用c3p0+JdbcUtils工具简 ...
- 《JavaWeb从入门到改行》很好的复习资料: SQL语句到底怎么写 ?
本文用到的数据库如下: CREATE DATABASE exam; /创建部门表/ CREATE TABLE dept( deptno INT PRIMARY KEY, dname ), loc ) ...
- 《JavaWeb从入门到改行》那些年一起学习的Servlet
目录 获取ServletContext : ServletContext接口中的一些方法 application域存取数据功能 代码演示: application域获取项目文件路径 代码演示: API ...
- 《JavaWeb从入门到改行》注册时向指定邮箱发送邮件激活
javaMail API javaMail是SUN公司提供的针对邮件的API . 两个jar包 mail.jar 和 activation.jar java mail中主要类:javax.mail. ...
- 《JavaWeb从入门到改行》多重外键关系在java中的处理方案
目录:(点击红色方框展开子目录) 问题描述 无 项目案例说明 业务描述 数据库说明 项目源码及下载 无 问题描述 如上两图,数据库中各个表之间有很多的外键关系,其中业务关系是一个用户下有该用户的订单, ...
- 《JavaWeb从入门到改行》关于BaseServlet那些事
@为什么需要BaseServlet? 我们知道一个POST或者GET提交对应着一个Servlet, 无数的提交会让Servlet页面增加,我们希望一个Servlet就能处理很多提交的请求. @Bas ...
- 《JavaWeb从入门到改行》分页功能的实现
@目录 什么是分页 ? 两个子模块功能的问题分析 和 解决方案 有条件查和无条件查询的影响 和 解决方案 项目案例: mysql + commons-dbutils+itcast-tools+Base ...
随机推荐
- css编写规范最佳实践
最初,在编写CSS的时候,我们往往想到哪儿就写到哪儿,它们之间的关联性和有序性并不在考虑之中.但随着代码量的增加,亦或是多人共同开发,CSS的编写规范变得重要起来了.本文通过三个方面,总结出CSS编写 ...
- 不信任的 .exe 怎么办,用 Windows 沙盒啊!
简评:维基百科,在计算机安全领域,沙盒(sandbox)是种安全机制,为执行中的程式提供的隔离环境.通常是作为一些来源不可信.具破坏力或无法判定程序意图的程序提供实验之用. 微软正在尝试解决人们对运行 ...
- UDP的优点
UDP优点 关于何时.发送什么数据的应用层控制更为精细 只需要应用层把数据传给UDP,UDP就把数据打包到网络层.对于TCP来说,存在一个拥塞控制机制,当链路变得拥塞时,会抑制TCP发送方,并造成数据 ...
- 多并发编程基础 之进程 Process
原贴 https://www.cnblogs.com/gbq-dog/p/10299663.html 1. 进程的理论知识 1.1 操作系统的背景知识 顾名思义,进程即正在执行的一个过程.进程是对正 ...
- Spring表单验证
表单验证 给表单添加验证的步骤如下 1.在 pom.xml 里添加 hibernate-validator 依赖http://hibernate.org/validator/documentation ...
- linux 多线程之间信号传递
函数 sigwait sigwait的含义就如同它的字面意思:等待某个信号的到来.如果调用该函数的线程没有等到它想等待的信号那么该线程就休眠.要达到等到一个信号,我们得做下面的事: 首先,定义一个信号 ...
- string、char *的转换
string转char* 主要有三种方法可以将str转换为char*类型,分别是:data(); c_str(); copy(); data()方法 string str = "hello& ...
- 常见的http错误提示
1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码. 代码 说明100 (继续) 请求者应当继续提出请求.服务器返回此代码表示已收到请求的第一部分,正在等待其余部分. 101 (切换协议 ...
- 第三方库PIL
第三方库PIL 一.Python简介 Python是一门简洁高效.通俗易懂的高阶动态编程语言,也可以理解成是一种面向对象的解释型计算机程序设计语言. Python具有丰富和强大的库.也经常被行内人员称 ...
- Linux系统编程:socket网络编程(操作篇)
一.问题思考 问1.网络通信应用在什么场合?通信的前提是什么? 答1.主要应用在不同主机进程间的互相通信,同一主机的进程也可以使用网络进行通信.通信的前提是如何标识通信进程的唯一,由于不同主机的进程极 ...