(20)JavaEE文件上传与下载

时间:2020-11-26 21:00:11

一、文件上传

1.如何从浏览器将文件的内容提交给服务器
表单必须是POST提交
文件选择框必须有name属性,因为表单中没有name属性的输入项是不会被提交的
表单必须设置enctype属性,属性的值设置为 multipart/form-data 

<form action="${pageContext.request.contextPath }/servlet/UploadServlet1" method="post"
enctype="multipart/form-data">
<span style="white-space:pre"></span> 选择文件:
<input type="file" name="upload"/>
<input type="submit" value="上传"/>
</form>

2.服务器收到数据后如何处理数据
commons - fileupload

(1) DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:
DiskFileItemFactory factory = new DiskFileItemFactory();
setSizeThreshold(int sizeThreshold)  //--设置内存缓冲区的大小的
setRepository(java.io.File repository) 
//--设置临时文件夹的位置,当上传文件的大小大于了内存缓冲区的大小,

      //上传文件时就不再使用内存缓冲区而是使用临时文件夹中的临时文件来作为缓冲使用

(2)ServletFileUpload
获取:new ServletFileUpload(factory);
boolean isMultipartContent(HttpServletRequest request)判断上传表单是否为multipart/form-data类型
List parseRequest(HttpServletRequest request)解析request对象,

       并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。

setFileSizeMax(long fileSizeMax)设置单个上传文件的最大值
setSizeMax(long sizeMax) 设置上传文件总量的最大值

设置编码格式,解决上传文件名乱码问题
setHeaderEncoding(java.lang.String encoding)
setProgressListener(ProgressListener pListener)实时监听文件上传状态

(3)FileItem 
boolean isFormField() 判断FileItem是一个文件上传对象还是普通表单对象
如果是一个普通字段项:
获取字段名:item.getFieldName()
获取字段值:item.getString()
使用commons-fileupload时上传的字段项的乱码通过request.setCharacterEncoding()是不能解决的

                如果要解决文件上传时普通字段项的乱码 item.getString(String encode)
如果是一个文件上传项:
文件名: item.getName();
文件的内容流: item.getInputStream();
item.delete() 在关闭FileItem输入流后,删除临时文件

3.文件上传保存位置问题:
(1)上传的文件必须保存在WEB-INF目录下保护起来,防止外界直接访问
upload="WEB-INF/upload"  temp="WEB-INF/temp"

(2)文件防止文件名重复造成的互相覆盖的问题,我们需要用UUID处理文件名,
  保证文件名绝对不会重复
  fname = UUID.randomUUID.toString()+"_"+fname;


(3)为了防止一个文件夹中文件过多的问题,需要将文件分目录存储,分目录的算法很多:Hash值分目录存储

String hash = Integer.toHexString(fname.hashcode);
for(char c: hash){
upload += "/"+c;
}
new File(this.getServletContext().getRealPath(upload)).mkdirs();

4.多文件上传表单

1.就是通过js来实现点击添加按钮,就可自动添加一个上传文件按钮,实现多个文件上传功能

//点击添加一个上传文件按钮
function addone(){
var s = document.getElementById("mydiv");
s.innerHTML +=
"<div><input type='file' name='filex' />"+
"<input type='button' value='不要了~~~' onclick='delOne(this)'/><br>"+
"</div>";
}
//删除按钮
function delOne(obj){
obj.parentNode.parentNode.removeChild(obj.parentNode);
}


5文件上传监听器

<span style="font-family: Arial, Helvetica, sans-serif;">//4--设置文件上传进度监听</span>

   ....fileload.setProgressListener(new ProgressListener() {
//pBytesRead已上传的字节数,pContentLength总大小,pItems列表项
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.print("读取字段项:"+pItems);
long nowTime = System.currentTimeMillis();
long time = (nowTime-startTime)/1000;
System.out.print("已用时:"+time+"秒 ,");
double per = getDouble(pBytesRead*1.0/pContentLength*100,2);
System.out.print("已上传:"+per+"%");
double speed = getDouble(pBytesRead*1.0/1024/time,2);
System.out.print("读取速度:"+speed+"KB/秒");
double endtime = getDouble((pContentLength-pBytesRead)*1.0/1024/speed,0);
System.out.println("距上传完毕约:"+endtime+"秒");
}
});
* 文件读取进度%保留2未小数处理
* @param src
* @param count
* @return
*/
private double getDouble(double src,int count){
return Math.ceil(src*Math.pow(10.0, count))/(Math.pow(10.0, count));
}

 

二、文件下载

1.将要下载的数据打给浏览器,并设置响应头,如果文件名中有中文,

   则要将文件名进行URL编码,注意编码的码表必须是UTF-8   response.setHeader("Content-Disposition", "attachment;"+"filename=" +URLEncoder.encode(fname, "UTF-8"));2.另外推荐大家 设置 一下 response.setContentType("xxx")

    其中设置下载的文件的MIME类型,明确的通知浏览器当前下载的数据是什么格式的, 方便浏览器处理.

3.下载方式一:使用IO流进行读写方式二:request.getRequestDispatcher("服务器上文件路径+name").forward(request,response);

案例:

1.总结前面的内容,实现一个可供上传,下载功能的简单云网盘列子:

   实现功能:1.上传文件2.显示可下载的资源信息id,name,上传时间,上传者Ip,信息3.下载技术包含:1.文件上传下载--乱码|名称唯一|分级目录|转发下载2.上传信息保存在数据库--BeanUtils数据封装2.文件信息在数据库中的CRUD

1.数据库创建

create database day20; use day20; create table netdisk( id int primary key auto_increment, uuidname varchar(255), realname varchar(255), savepath varchar(255), uploadtime timestamp, ip varchar(100), description varchar(255)

);

2.jar包,实体类,jsp页面,工具类,C3P0数据源配置文件     

(20)JavaEE文件上传与下载

详细设计(核心代码):

1.domain实体类

  实体类属性:按javabean规范编写

 private int id;
private String uuidname; //上传文件的名称,文件的uuid名
private String realname; //上传文件的真实名称
private String savepath; //记住文件的位置
private Timestamp uploadtime; //文件的上传时间
private String ip;//上传者IP
private String description; //文件的描述

2.JSP界面

(a) index.jsp    主页

  <body style="text-algin:center">
<h1>我的云盘,乐在分享</h1><hr>
<a href="${pageContext.request.contextPath }/upload.jsp">上传资源</a>
<a href="${pageContext.request.contextPath }/servlet/DownsServlet">下载资源</a>
</body>
(b)upload.jsp     上传界面

 <h1>乐在分享</h1><hr color="red">
<form action="${pageContext.request.contextPath}/servlet/UploadServlet" method="POST"
enctype="multipart/form-data">
选择文件:<input type="file" name="filex"/><br>
<b>资源描述:</b><br>
<textarea rows="5" cols="40" name="description"></textarea><br>
<input type="submit" value="上传"/>
</form>
(c)downs.jsp   下载界面--显示可供下载资源
<span style="white-space:pre"></span><h1>下载列表</h1><hr color="black" size="3">
<c:forEach items="${requestScope.list }" var="s">
<b>资源ID:</b>${s.id }<br>
<b>资源名称:</b>${s.realname }<br>
<b>上传时间:</b>${s.uploadtime }<br>
<b>上传者:</b>${s.ip }<br>
<b>资源描述:</b>${s.description }<br>
<a href="${pageContext.request.contextPath }/servlet/DownloadServlet?id=${s.id}">下载</a>
<br>
<hr color="red">
</c:forEach>

3.Daoutils工具类

public class DaoUtils {

private static DataSource source = new ComboPooledDataSource();
private DaoUtils(){}

/**
* 获取C3P0数据源
* @return
*/
public static DataSource getSource(){
return source;
}

/**
* 获取连接对象
* @return
*/
public static Connection getConn(){
try {
return source.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}


4.上传的Servlet:负责实现上传功能

public class UploadServlet extends HttpServlet {

@SuppressWarnings({ "static-access", "unchecked" })
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
String upload ="WEB-INF/upload";
String temp = "WEB-INF/temp";
Map<String,String> map = new HashMap<String,String>();
try {
//1------------将文件上传到服务器-----------------------------------》
//1.1创建上传文件的工厂类
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024*80);
factory.setRepository(new File(this.getServletContext().getRealPath(temp)));
//1.2获取上传文件的核心类
ServletFileUpload fileload = new ServletFileUpload(factory);
fileload.setHeaderEncoding("UTF-8");
fileload.setFileSizeMax(1024*1024*100);
fileload.setSizeMax(1024*1024*200);
if(!fileload.isMultipartContent(request)){
throw new RuntimeException("表单错误!不是文件上传类型");
}
//3.解析文件数据
List<FileItem> list = fileload.parseRequest(request);
for(FileItem item :list){
if(item.isFormField()){//普通字段
String name = item.getFieldName();
String val = item.getString("UTF-8");
map.put(name, val);
}else{
String realname = item.getName();
map.put("realname",realname);
//编码文件名
String uuidname = UUID.randomUUID().toString()+"_"+realname;
map.put("uuidname",uuidname);
//文件分目录存储
String hash = Integer.toHexString(uuidname.hashCode());
for(char c:hash.toCharArray()){
upload += "/"+c;
}
map.put("savepath", upload);
//创建目录结构
new File(this.getServletContext().getRealPath(upload)).mkdirs();

InputStream is = item.getInputStream();
OutputStream os = new FileOutputStream(this.getServletContext()
.getRealPath(upload)+"/"+uuidname);
byte [] b = new byte[1024];
int len = 0;
while((len=is.read(b))!=-1){
os.write(b, 0, len);
}
is.close();
os.close();
item.delete();
}
}
//------------2将文件信息保存到数据库中---------------------------》
//以下发生错误时,会调转到isMultipartContent()--判断中抛出的异常--
Resource res = new Resource();
map.put("ip",request.getRemoteAddr());
BeanUtils.populate(res,map);//封装数据
//
String sql ="insert into netdisk values(null,?,?,?,null,?,?)";
QueryRunner qr = new QueryRunner(DaoUtils.getSource());
qr.update(sql,res.getUuidname(),res.getRealname(),res.getSavepath(),res.getIp(),res.getDescription());

//------------3返回到首页----------------------------------------》
response.getWriter().write("上传成功!3秒后回到主页!");
response.setHeader("Refresh", "3;URl="+request.getContextPath()+"/index.jsp");

}catch(FileSizeLimitExceededException e){
response.getWriter().write("文件大小不能超过100M!");
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}

}


5.显示下载列表的Servlet

public class DownloadServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try {
//1.获取需要下载的资源id
String id = request.getParameter("id");
//2.从数据库查找需要下载的资源
String sql ="select * from netdisk where id=?";
QueryRunner qr = new QueryRunner(DaoUtils.getSource());
Resource res = qr.query(sql, new BeanHandler<Resource>(Resource.class),id);
//3.通知浏览器需要进行下载
response.setHeader("Content-Disposition", "attachment;filename="
+URLEncoder.encode(res.getUuidname(),"UTF-8"));
//让浏览器自动获取资源MIME类型
response.setContentType(this.getServletContext().getMimeType(res.getRealname()));
//4.使用转发跳转到资源地址进行下载
request.getRequestDispatcher("/"+res.getSavepath()+"/"+res.getUuidname())
.forward(request, response);
} catch (SQLException e) {
e.printStackTrace();
}


}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
6.进行下载的Servlet

public class DownsServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try {
//1.获取QueryRunner对象,创建sql语句
QueryRunner qr = new QueryRunner(DaoUtils.getSource());
String sql = "select * from netdisk ";
//2.查询数据库中所有可下载资源
List<Resource> list = qr.query(sql, new BeanListHandler<Resource>(Resource.class));
for(Resource s:list){
System.out.println(s);
}
//3.将得到的list集合放到request域中
request.setAttribute("list", list);
//4.转发到下载列表界面
request.getRequestDispatcher("/downs.jsp").forward(request, response);
} catch (SQLException e) {
e.printStackTrace();
}
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}

这样一个简单的上传下载网盘功能就实现了。