在制作下载web页面时,需要列出服务器可供下载的所有文件;为了能够在页面上显示这些文件,可以将这些页面文件的文件名存储到一个map集合。map集合的key值为文件在服务器上真实的存储名称,value值为文件的真实名称。下载web页面可以通过超链接直接提供下载。
文件列表示例代码:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>下载列表</title>
</head>
<body>
<c:forEach var="me" items="${map}">
<c:url value="/servlet/DownloadServlet" var="downurl">
<c:param name="filename" value="${me.key }"></c:param>
</c:url>
${me.value }<a href="${downurl }" >下载</a><br />
</c:forEach>
</body>
</html>
由于文件名可能存在中文名称,这个时候通过超链接提供下载,需要向处理下载的servlet提供下载的文件名。为了避免产生中文乱码问题,需要对文件名进行URL编码,这可以使用jstl标签库中的<c:url>和<c:param>标签,前者将产生一个URL地址,后者为这个地址添加一个filename文件名的参数。具体如下:
<c:url value="/servlet/DownloadServlet" var="downurl">
<c:param name="filename" value="${me.key }"></c:param>
</c:url>
downurl将是包含一个参数为filename的url访问地址,filename中的中文将会进行URL编码。
借鉴文件上传代码,此处需要产生共文件下载列表页面显示的map容器,因此需要对存放文件的目录进行扫描,将其中所有文件(不是文件夹)的存储文件名和真实文件名加入到map中。下列代码中的makeFileList将完成此功能,他将会将file文件下的所有文件的相关信息加入到map容器中。
ListServlet代码:
public class ListServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Map<String, String> map = new HashMap<String, String>();
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
File file = new File(savePath);
if(!file.exists()){
request.setAttribute("message", "对不起,没有上传文件");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
makeFileList(file, map);
request.setAttribute("map", map);
request.getRequestDispatcher("/downlist.jsp").forward(request, response);
}
//将file文件夹中所有文件加入到map容器中,map的key值为文件真实存储名,value为文件原始名
private void makeFileList(File file, Map<String, String> map) {
// TODO Auto-generated method stub
File[] files = file.listFiles();
for(int i=0; files!=null && i<files.length; i++){
if(!files[i].isFile()){
makeFileList(files[i], map);
}else{
String saveName = files[i].getName();
map.put(saveName, saveName.substring(saveName.indexOf("_")+1));
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
由于请求体带过来的url地址的参数是经过url编码的,因此在获取filename参数时,需要先对其进行iso8859-1解码,再用指定编码方式编码。才可以的到期原始值。另外通过filename计算出此文件在服务器存储路劲中的存储目录。要想在下载页面上实现下载保存效果,需要设置response的head头和content-type。
程序实现下载需设置两个响应头:
设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type.
下载示例代码:
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
String fileName = (String) request.getParameter("filename");
fileName = new String(fileName.getBytes("iso8859-1"), "utf-8");//filename参数值进行了url编码,需要先进行iso8859-1解码,在进行utf-8编码
System.out.println(fileName);
String realName = fileName.substring(fileName.indexOf("_") + 1);//获取文件的真实名称
savePath = getSavePath(savePath, fileName);//通过文件打散算法,利用文件存储名获取其在服务器中的存储路径
File f = new File(savePath+"\\"+fileName);
if(!f.exists()){
request.setAttribute("message", "文件已经被删除");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
response.setContentType("application/x-msdownload");
response.setHeader("content-disposition", "attachment;filename="+realName);//实现文件下载保存效果
FileInputStream in = new FileInputStream(savePath+"\\"+fileName); //读取文件并写入到浏览器中
OutputStream out = response.getOutputStream();
byte[] buf = new byte[1024];
int len=0;
while((len=in.read(buf))>0){
out.write(buf, 0, len);
}
in.close();
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
// 文件打散问题,解决一个文件下文件过多的问题
private String getSavePath(String savePath, String fileName) {
if (fileName == null || savePath == null) {
return null;
}
int hCode = fileName.hashCode();
int dir1 = hCode & 0x0f;
int dir2 = (hCode & 0xf0) >> 4;
String dir = savePath + "\\" + dir1 + "\\" + dir2;
// 文件要是不存在,创建此文件
File f = new File(dir);
if (!f.exists()) {
return null;
}
return dir;
}
}
注意细节:
1.提交request请求消息体带有的参数含有中文数据时,需要对其进行URL编码
2.对URL进行编码的,不能直接取出其值,需要进行iso8859-1解码,在用指定的编码方式编码
3.需要设置response head的content-disposition头,值为attachment;filename=文件名
response.setHeader("content-disposition", "attachment;filename="+realName);//实现文件下载保存效果
4.设置content-type值为application/x-msdownload
response.setContentType("application/x-msdownload");
需注意的是,在设置content-disposition之前一定需要先设置content-type
5.解析文件时需要通过文件名来获取其存储路径