一、文件上传
文件上传的表单的method必须是post方式 表单的enctype必须是multipart/form-data 默认值是application/x-www-form-urlencoded enctype作用是告知服务器请求正文类型
对请求正文是multipart/form-data类型的数据进行解析
application/x-www-form-urlencoded请求的正文是这样的 username=lsy&password=123
multipart/form-data 要获取上传的文件要用request.getInputStream获取正文是这样的
<span style="color: rgb(51, 51, 255); white-space: pre;"> </span>------WebKitFormBoundary3hgN1BZho1trLAgj
Content-Disposition: form-data; name="file"; filename="a.txt"
Content-Type: text/plain
6666 <span style="color:#ff0000;"> //6666才是文件的结果</span>
二、使用第三方包实现上传
使用第三方包commons-fileupload、commons-io-2.0.1.进行文件上传
常用的类
DiskFileItemFactory:产生FileItem的工厂类
new DiskFileItemFactory(int sizeThreshold, File repository)
sizeThreshold:指定一个缓存大小。默认大小10Kb
repository:磁盘临时文件的存放目录。默认是当前用户的系统临时文件目录
new DiskFileItemFactory()
ServletFileUpload:核心解析器
FileItem:代表着一个表单上传输入域
public class UploadServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
//创建对象
DiskFileItemFactory factory =new DiskFileItemFactory();
<span style="color:#ff0000;">//判断用户表单的enctype属性是不是multipart/form-data类型</span>
boolean isMultipart=ServletFileUpload.isMultipartContent(request);
if(!isMultipart){
throw new RuntimeException("请求的信息不是multipart类型");
}
//创建ServletFileUpload的实例
ServletFileUpload sf=new ServletFileUpload(factory);
//解析请求对象
try{
List<FileItem> items=sf.parseRequest(request);
for(FileItem item:items){
if(item.isFormField()){
//如果是普通字段
processFormField(item);
}else{
//如果是上传字段
processUploadField(item);
}
}
response.getWriter().write("上传成功");
}catch(FileUploadException e){
throw new RuntimeException("解析请求正文失败,请换一个浏览器试试");
}
}
<span style="color:#ff0000;">//上传字段处理方式</span>
private void processUploadField(FileItem item) {
//把上传文件存到/files目录中
String realPath = getServletContext().getRealPath("/files");
File storeDirectory = new File(realPath);
if(!storeDirectory.exists()){
//如果这个文件夹不存在就创建
storeDirectory.mkdir();
}
//获取文件名
String filename=item.getName();<span style="color:#ff0000;"<span style="color:#ff0000;">>//获取文件上传的名称 根据浏览器的不同可能是 d:\a.txt 或者a.txt</span></span>
if(filename!=null){
filename=FilenameUtils.getName(filename);
<span style="color:#ff0000;"><span style="color:#ff0000;">//这个方法于这些代码相同</span> filename=filename.subString(filename.lastIndexOf("\\")+1);</span>
}
//上传
try{
item.write(new File(realPath,filename));
<span style="color:#ff0000;"><span style="color:#ff0000;">/*这个方法于这些代码相同</span></span>
InputStream in = item.getInputStream();
OutputStream out = new FileOutputStream(realPath+"//"+filename);
int len = -1;
byte b[] = new byte[1024];
while((len=in.read(b))!=-1){
out.write(b, 0, len);
}
in.close();
out.close();
*/
}catch(Exception e){
System.out.println("文件上传失败");
}
}
//普通字段处理方式
private void processFormField(FileItem item) {
String fieldName=item.getName();
String fieldValue=item.getString();
System.out.println(fieldName+fieldValue);
}
三、文件上传时需要注意的问题
1.服务器的安全 用户上传jsp文件 会运行jsp里面的代码 这是非常危险的
解决方法 a、把上传文件放到用户无法直接访问的地方(web-inf)
b、限制用户上传文件类型
2、重名文件被覆盖问题 每次相同的文件名上传会覆盖上次的
解决方法a、让文件名唯一
在文件名前加GUID
3、避免上传文件都存到一个目录中
解决方法a、按日期产生目录,把当天文件存到到目录中
b、按GUID文件名hash计算存放目录。
4、中文乱码问题
解决方法 普通字段、FileItem.getString(“浏览器的编码");
中文文件名、request.setCharacterEncoding("UTF-8");//指示
5、限制文件上传类型
通过判断文件的mime类型和文件的扩展名来限制上传的文件 可以在tomcat/conf/web.xml 中查看那些后缀名的文件属性那些类型
6、限制上传文件的大小
单个文件大小限制:
ServletFileUpload.setFileSizeMax(long size);
抓取的异常
提示:FileUploadBase.FileSizeLimitExceededException
总文件大小限制
ServletFileUpload.setSizeMax(long size)
抓取的异常
提示:FileUploadBase.SizeLimitExceededException
7、临时文件的问题
下载 临时文件是暂时存储 当流传输完后就要 FileItem.delete();//删除临时文件 这是自己手动用流实现才需要
如果使用第三方包是不需要的 因为 FileItem.write()这方法封装了 删除临时文件
改进版后的文件上传
public class UploadServlet3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
DiskFileItemFactory factory = new DiskFileItemFactory();
<span style="color:#ff0000;">// 设置临时文件存放目录</span>
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
throw new RuntimeException("表单不是multipart");
}
ServletFileUpload sfu = new ServletFileUpload(factory);
<span style="color:#ff0000;">// sfu.setFileSizeMax(3*1024*1024);//上传文件的大小
sfu.setSizeMax(5*1024*1024);//总文件大小</span>
try {
List<FileItem> items = sfu.parseRequest(request);//获取的上传文件 是一个集合
for (FileItem item : items) {//循环多个上传域
if (item.isFormField()) {
// 普通字段
processFormField(item);
} else {
if (item == null) {
continue;
}
processUploadField(item);
}
}
response.getWriter().write("上传成功");
}catch(FileUploadBase.FileSizeLimitExceededException e){
response.getWriter().write("文件超出大小限制");
}catch(FileUploadBase.SizeLimitExceededException e){
response.getWriter().write("一次上传文件不能超过5M");
}catch (FileUploadException e) {
e.printStackTrace();
throw new RuntimeException("解析请求正文失败,请换一个浏览器试试");
}
}
<span style="color:#ff0000;">// 上传域处理方式</span>
private void processUploadField(FileItem item) {
String mimeType = item.getContentType();// 获取上传的类型 // html---->text/html // jpeg--->image/jpeg // (Tomcat\conf\web.xml这个文件下可以查看那些类型对应那些文件)
if(!mimeType.startsWith("image")){
<span style="color:#ff0000;">//如果上传不是图片
System.out.println("只能上传图片");</span>
}else{
String realPath=getServletContext().getRealPath("/WEB-INF/files");
File storeDirectory=new File(realPath);
if(!storeDirectory.exists()){
storeDirectory.mkdir();
}
String filename=item.getName()<span style="color:#ff0000;">;//获取上传文件名 //浏览器的不同c:\desktop\a.txt or a.txt</span>
if(filename!=null){ <span style="color:#ff0000;">//避免文件重名文件名+GUID</span>
filename=GUID.generateGUID()+"_"+FilenameUtils.getName(filename);
}
<span style="color:#ff0000;">//使上传文件存放目录根据日期文件夹存储</span>
//String childDirectory=makeChildDirectory(realPath);
<span style="color:#ff0000;">//文件名的hash码计算目录:分2级子目录存储</span>
String childDirectory=makeChildDirectory(realPath,filename);
try {
item.write(new File(realPath+"/"+childDirectory,filename));
} catch (Exception e) {
throw new RuntimeException("文件上传失败");
}
}
}
<span style="color:#ff0000;">//用文件名的hash码计算目录:分2级子目录</span>
private String makeChildDirectory(String realPath, String filename) {
int hashCode=filename.hashCode();
int dir1=hashCode&0xf; <span style="color:#ff0000;">//一级文件夹 最多16个</span>
int dir2=(hashCode&0xf0)>>4;//二级文件夹最多16个
String childDirectory=dir1+"/"+dir2;
File file =new File(realPath,childDirectory);
if(!file.exists()){
file.mkdirs();
}
return childDirectory;
}
<span style="color:#ff0000;">//使上传文件存放目录根据日期文件夹存储</span>
private String makeChildDirectory(String realPath) {
Date now =new Date();
DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
String date=df.format(now);<span style="color:#ff0000;">//解析过的日期</span>
File file =new File(realPath,date);
if(!file.exists()){
file.mkdirs();
}
return date;
}
// 普通域处理方式
private void processFormField(FileItem item)
throws UnsupportedEncodingException {
String fieldName = item.getFieldName();
String fieldValue = item.getString("UTF-8");// 如果form中的参数有中文,用这个方法会乱码
// uft-8为编码方式
System.out.println(fieldName + ":" + fieldValue);
}
Servlet规范中的8个监听器
a、监听ServletContext、HttpSession、ServletRequest对象的创建和销毁的监听器
ServletContextLIstener;
HttpSessionListener; 作用: 想知道有多少个并发用户,也就是说,你想跟踪活动的会话
ServletRequestListener; 作用:每次请求到来时你都知道,以便建立日志记录
分别都有2个方法 监听创建和销毁
b、监听ServletContext、HttpSession、ServletReques属性(attribute)变化的监听器
ServletContextAttributeListener; 作用:想知道一个web应用上下文中是否增加、删除或替换了一个属性
HttpSessionAttributeListener;
ServletRequestAttributeListener 作用:增加、删除或替换一个请求属性时
分别都有三个方法 setAttribute(第一次添加,第二次修改attributeReplaced()) 删除的时候removeAttribute()
c、HttpSessionBindingListener:实现这个接口类的实例
作用:有一个属性类,希望这个类型的对象在绑定到一个会话或从会话中或从会话删除时得到通知
HttpSessionActivationListener
作用:有一个属性类,这个类型的对象在其绑定的会话迁移到另一个JVM时得到通知
C类监听器不需要注册
注册监听器方法
注册一个类型实现监听器接口 web.xml中配置如下
<listener>
<listener-class>test.listener.MyServletRequestListener</listener-class>
</listener>