web文件上传

        今天在复习Web 的时候,做个一个文件上传的笑Demo,发布一边博客,记录一些笔记要点。和web做上传的一些注意事项。这个Demo 解决了Web上传问题中的一下问题:

1.实现web开发中的文件上传功能,需要完成2个步骤操作:

①在web页面中添加上传输入项
②在servlet中读取上传文件的数据,并保存到本地盘中。

如何在web页面中添加上传输入项?
<input type="file">标签用于在web页面中添加文件上传输入项,设 置文件上传输入项时刻须注意。
1.必须要设置inpu输入项的name属性,否则浏览器将不会发送上传文件的数据.
2.必须把form的enctype属性设置为multipart/form-data,设置该值后,浏览器在上传文件时,将把文件数据附带在http
请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。

DisKFileItemFactory是创建 FileItem 对象的工厂,这个工厂类常用方法:
public void setSizeThreshold(int size Threshould)
设置内存缓冲区的大小,默认为10k,当上传文件大于缓冲区大小时,fileupload组件将使用
临时缓存上传文件。
public void  (java.io.File.repository)
指定临时文件目录,默认值为
System.getProperty("java.lang.tmpdir");

public DiskFileItemFactory(int size Threshould,java.ioo.File repository)
构造函数

上传文件乱码问题:
DiskFileItemFactory factory = new DiskFileItemFactoy();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setEncoding("UTF-8");

inpuvalue = new String(inputValue.getBytes("iso-8859-1","UTF-8"));

ServletFileUpload.setHeaderEnconding("UTF-8");
1.上传文件乱码:
1.1 ServletFileUpload.setHeaderEnconding("UTF-8");
1.2 解决普通输入项的乱码(注意:表单类型为Multipart/form-data的时候,设置request的编码是无效的)
FileItem.setString("UTF-8"); //解决乱码
2.在处理表单之前,要记得调用:
ServletFileUpload.isMultipartContent
3.设置解析器缓冲器大小,以及临时文件的删除
设置解析器缓冲区大小   DiskFileItemFactory.setSizeThreshould(1024*1024);方法
临时文件的删除,在程序中处理完成上传文件后,一定要记得调用item.delete()方法,以删除临时文件

4.再上传系统时候,注意上传文件的目录,这个文件的保存目录不能让外界直接访问到.不然后服务器有很大的漏洞。

5.限制上传文件的类型
List types = Arrays.asList(".jpg",".gif",".avi",".txt"); 
6.限制上传文件的大小
ServletFileUpload负责处理上传的数据,并将表单中每个输入项封装成一个FileItem对象中,常用方法有:
boolean isMultipartContent(HttpServletRequest request)
判断上传表单是为Multipart/form-data类型
List<FileItem> list = upload.parseRequest(request);
解析request对象,并把表单中的每个输入项包装成一个fileItem对象,并返回一个保存了所有FileItem的list集合.
setFile SizeFileMax(long fileSizeMax)
设置单个上传文件总量的最大值
setSizeMax(long sizeMax)
设置上传文件总量的最大值
setHeaderEconding(java.lang.String encoding)
设置编码格式
setProgressListener(ProgressListener pListener)

7.如何判断空上传输入项
String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //获取到文件名称
String ext = filename.substring(filename.lastIndexOf(".")+1); //获得文件的扩展名
if(filename==null || filename.trim().equals("")){
continue;
}

8.为避免上传文件的覆盖,程序在保存上传文件时,要为文件生成唯一的文件名
public String generateFileName(String filename){
return UUID.randomUUID().toString() + "_" + filename;
}

String savepath = this.getServletContext().getRealPath("/WEB-INF/upload");
String saveFileName = generateFileName(filename);
//File.separator   常量分隔符   /
FileOutputStream out = new FileOutputStream(savepath + File.separator + saveFileName); //指定目录下

9.目录文件上限问题(哈希目录)  原理:文件名转换成哈希值  根据哈希第四位生成一级目录
在web工程中难免会有目录保存的文件过多而见降低打开目录的效率.一般来说一个目录保存1000个文件属于正常。对于1000个,目录打开效率就会降低
所以我们使用一种将目录的名字转换成哈希值打散存储
public String generateSavePath(String pathe,String filename){
int hashcode = filename.hashCode(); //121212
int dir1 = hashcode&15;
int dir2 = (hashcode>>4)&0xf;

String savepath = path + File.separator + dir1 + File.separator + dir2;
if(!file.exists()){
file.mkdirs(); //因为需要创建多级目录  所以这里一定是mkdirs()  不是mkdir()函数
}
return savepath;
}

图示举例:
哈希值在计算机中的保存以二进制保存   为数字

0011 1010 0111 1111 1010 1110 0110 0111
---------------------------------------    二进制 1111 = 15 十进制
0000 0000 0000 0000 0000 0000 0000 1111

结果:000 0000 0000 0000 0000 0000 0000 0111 7
按照哈希值的第四位 保存 二进制0111 = 十进制 7 保存到文件目录为7的目录下,第四位最大为15-最小为0,故总共16个一级目录
一级目录不够,再利用哈希值的第5-8位,生成二级目录.类似可以生成三级目录、四级目录、无极目录... 代码如下:

//解决目录上限(1000个最佳)问题
public String generateSavePath(String path,String filename){
int hashcode = filename.hashCode(); //哈希值
int dir1 = hashcode&15; //一级目录
int dir2 = (hashcode>>4)&0xf; //二级目录  哈希值的第5~8位
String savepath = path + File.separator + dir1 + File.separator + dir2;
File file = new File(savepath);
if(!file.exists()){
file.mkdirs();
}
return savepath;
}

10.后台进度条数据(设置监听器 ServletFileUpload.setProcessListener())
(监听器一定要设在解析之前)使用解析器调用update方法
void update(long pBytesRead,long pContentLength,int pItems);
//每 1M 进度条涨一下
ProgressListener progressListener = new ProgerssListener(){
private long megaBytes = -1;
public void update(long pBytesRead,long pContentLength,int pItems){
long mBytes = pBytesRead / 1000000;
if(megBytes == aBytes){
return ;
}
megaBytes = mBytes;
System.out.println("We are currently reading item "+pItems);
if(pContenLength == -1){
System.out.println("So far "+pBytesRead +"Bytes have been read ");
}else{
System.out.println("So far."+pBytesRead +"of "+ pContentLength +"bytes have been read.");
}
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet{
/**

*/
private static final long serialVersionUID = 1L;
//处理上传数据
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
DiskFileItemFactory factory = new DiskFileItemFactory(); //创建一个工厂  默认缓冲区大小为10k
//设置缓冲区大小为 1 M  如果上传文件大于1M,则上传的文件将不再保存在缓冲区,将会保存在临时文件中,读取数据的时候也会从临时文件中读取
factory.setSizeThreshold(1024*1024);  
//设置临时文件保存目录   / 代表web工程    将/temp设置为临时保存文件目录
factory.setRepository(new File(this.getServletContext().getRealPath("temp")));
ServletFileUpload upload = new ServletFileUpload(factory); //创建解析器

//在解析数据之前  做后台页面数据生成进度条
upload.setProgressListener(new ProgressListener() {
private long megaBytes = -1 ;
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
long mBytes = pBytesRead / 1000000;
if(mBytes == -1){
return ;
}
megaBytes = mBytes;
System.out.println("已经读取文件个数:"+pItems);
if(pContentLength == -1){
System.out.println("已经读取"+pBytesRead+"字节");
}else{
System.out.println("读取了"+ pBytesRead +"字节");
}
}
});

//解决中文乱码问题
upload.setHeaderEncoding("UTF-8");
//限制单个文件上传大小   100 M
upload.setFileSizeMax(1024*1024*100);
//限制上传数据的类型
List types = Arrays.asList("txt","avi","gif","jpg");
if(!upload.isMultipartContent(request)){ //若果不是上传表单数据
String nameString = request.getParameter("username");
System.out.println("name:"+nameString);
return ;
}
try {
System.out.println("//////////////////////////////////");
System.out.println(this.getServletContext().getRealPath("/WEB-INF/upload"));
System.out.println("----------------------------------");
List<FileItem> list = upload.parseRequest(request); //调用解析器解析request,得到并保存所有上传数据的List
for (FileItem item : list) {
if(item.isFormField()){ //判断是否为输入项
//为输入项
String  inputName = item.getFieldName(); //获得文件的名称
String  inputvalue = item.getString(); //获得文件的值
String  inputValue = new String(inputvalue.getBytes("iso-8859-1"));
System.out.println(inputName+"="+inputValue);
}else{
//代表输入项中封装的是文件
String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //获取到文件名称
String ext = filename.substring(filename.lastIndexOf(".")+1); //获得文件的扩展名
if(filename==null || filename.trim().equals("")){
continue;
}
/*if(!types.contains(ext)){
request.setAttribute("message","本系统不支持上传"+ext+"文件类型");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return ;
}*/
System.out.println("filename:"+filename);
InputStream in = item.getInputStream(); //获得inputStream流
//设置缓冲区
int len = 0;
byte buffer[] = new byte[1024];
String saveFileName = generateFileName(filename); //调用gerenate函数 生成唯一的文件名称
String savepath = generateSavePath(this.getServletContext().getRealPath("/WEB-INF/upload"),saveFileName);
//File.separator   常量分隔符   /
FileOutputStream out = new FileOutputStream(savepath + File.separator + saveFileName); //指定目录下
while((len = in.read(buffer)) > 0){
out.write(buffer,0,len);
}
in.close();
out.close();
item.delete(); //删除上传完毕后的临时文件
}
}
}catch (FileUploadBase.FileSizeLimitExceededException e) {
request.setAttribute("message","文件大小不能超过 5 M");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return ;
}catch (FileUploadException e) {
e.printStackTrace();
}
}

//解决同名文件覆盖问题
public String generateFileName(String filename){
//UUID.randomUUID().toString() 74e59732-f339-4546-bf91-42c6c83696ea 全球唯一标识
return UUID.randomUUID().toString() + "_" + filename;
}

//解决目录上限(1000个最佳)问题
public String generateSavePath(String path,String filename){
int hashcode = filename.hashCode(); //哈希值
int dir1 = hashcode&15; //一级目录
//0x 代表16进制  16进制 0 1 2 . . . A(10) B C D E F(15) 则0xf = 1111 最高为补24位 0000 0000 0000 0000 0000 0000 1111 
//hashcode>>4 哈希值向前移动4位 若原来为0101 1000 0010 0001 1111 1100 1010 1111 移动后为0101 1000 0010 0001 1111 1100 1010
int dir2 = (hashcode>>4)& 0xf; //二级目录   
String savepath = path + File.separator + dir1 + File.separator + dir2;
File file = new File(savepath);
if(!file.exists()){
file.mkdirs();
}
return savepath;
}
}

};


1.创建Web工程



2.编写UploadServlet.java代码

package com.nyist.cn.Servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet{
/**

*/
private static final long serialVersionUID = 1L;
//处理上传数据
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
DiskFileItemFactory factory = new DiskFileItemFactory(); //创建一个工厂  默认缓冲区大小为10k
//设置缓冲区大小为 1 M  如果上传文件大于1M,则上传的文件将不再保存在缓冲区,将会保存在临时文件中,读取数据的时候也会从临时文件中读取
factory.setSizeThreshold(1024*1024);  
//设置临时文件保存目录   / 代表web工程    将/temp设置为临时保存文件目录
factory.setRepository(new File(this.getServletContext().getRealPath("temp")));
ServletFileUpload upload = new ServletFileUpload(factory); //创建解析器

//在解析数据之前  做后台页面数据生成进度条
upload.setProgressListener(new ProgressListener() {
private long megaBytes = -1 ;
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
long mBytes = pBytesRead / 1000000;
if(mBytes == -1){
return ;
}
megaBytes = mBytes;
System.out.println("已经读取文件个数:"+pItems);
if(pContentLength == -1){
System.out.println("已经读取"+pBytesRead+"字节");
}else{
System.out.println("读取了"+ pBytesRead +"字节");
}
}
});

//解决中文乱码问题
upload.setHeaderEncoding("UTF-8");
//限制单个文件上传大小   100 M
upload.setFileSizeMax(1024*1024*100);
//限制上传数据的类型
List types = Arrays.asList("txt","avi","gif","jpg");
if(!upload.isMultipartContent(request)){ //若果不是上传表单数据
String nameString = request.getParameter("username");
System.out.println("name:"+nameString);
return ;
}
try {
System.out.println("//////////////////////////////////");
System.out.println(this.getServletContext().getRealPath("/WEB-INF/upload"));
System.out.println("----------------------------------");
List<FileItem> list = upload.parseRequest(request); //调用解析器解析request,得到并保存所有上传数据的List
for (FileItem item : list) {
if(item.isFormField()){ //判断是否为输入项
//为输入项
String  inputName = item.getFieldName(); //获得文件的名称
String  inputvalue = item.getString(); //获得文件的值
String  inputValue = new String(inputvalue.getBytes("iso-8859-1"));
System.out.println(inputName+"="+inputValue);
}else{
//代表输入项中封装的是文件
String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //获取到文件名称
String ext = filename.substring(filename.lastIndexOf(".")+1); //获得文件的扩展名
if(filename==null || filename.trim().equals("")){
continue;
}
/*if(!types.contains(ext)){
request.setAttribute("message","本系统不支持上传"+ext+"文件类型");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return ;
}*/
System.out.println("filename:"+filename);
InputStream in = item.getInputStream(); //获得inputStream流
//设置缓冲区
int len = 0;
byte buffer[] = new byte[1024];
String saveFileName = generateFileName(filename); //调用gerenate函数 生成唯一的文件名称
String savepath = generateSavePath(this.getServletContext().getRealPath("/WEB-INF/upload"),saveFileName);
//File.separator   常量分隔符   /
FileOutputStream out = new FileOutputStream(savepath + File.separator + saveFileName); //指定目录下
while((len = in.read(buffer)) > 0){
out.write(buffer,0,len);
}
in.close();
out.close();
item.delete(); //删除上传完毕后的临时文件
}
}
}catch (FileUploadBase.FileSizeLimitExceededException e) {
request.setAttribute("message","文件大小不能超过 5 M");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return ;
}catch (FileUploadException e) {
e.printStackTrace();
}
}

//解决同名文件覆盖问题
public String generateFileName(String filename){
//UUID.randomUUID().toString() 74e59732-f339-4546-bf91-42c6c83696ea 全球唯一标识
return UUID.randomUUID().toString() + "_" + filename;
}

//解决目录上限(1000个最佳)问题
public String generateSavePath(String path,String filename){
int hashcode = filename.hashCode(); //哈希值
int dir1 = hashcode&15; //一级目录
//0x 代表16进制  16进制 0 1 2 . . . A(10) B C D E F(15) 则0xf = 1111 最高为补24位 0000 0000 0000 0000 0000 0000 1111 
//hashcode>>4 哈希值向前移动4位 若原来为0101 1000 0010 0001 1111 1100 1010 1111 移动后为0101 1000 0010 0001 1111 1100 1010
int dir2 = (hashcode>>4)& 0xf; //二级目录   
String savepath = path + File.separator + dir1 + File.separator + dir2;
File file = new File(savepath);
if(!file.exists()){
file.mkdirs();
}
return savepath;
}

}

4.上传文件测试



运行结果:

............

............

............

已经读取文件个数:3

读取了83787776字节
已经读取文件个数:3
读取了83791830字节
已经读取文件个数:3
读取了83795884字节
已经读取文件个数:3
读取了83795968字节
已经读取文件个数:3
读取了83800022字节
已经读取文件个数:3
读取了83804076字节
已经读取文件个数:3
读取了83804160字节
已经读取文件个数:3
读取了83808214字节
已经读取文件个数:3
读取了83812268字节
已经读取文件个数:3
读取了83812352字节
已经读取文件个数:3
读取了83816406字节
已经读取文件个数:3
读取了83820460字节
已经读取文件个数:3
读取了83820544字节
已经读取文件个数:3
读取了83824598字节
已经读取文件个数:3
读取了83828652字节
已经读取文件个数:3
读取了83828736字节
已经读取文件个数:3
读取了83831201字节
username=监听器上传视频数据

filename:02-处理上传需要注意的细节.avi


猜你喜欢

转载自blog.csdn.net/lvhaoguang0/article/details/81011371