实现文件的上传与下载,可以使用Java的I/O流的类来实现,也可以使用专业的上传、下载组件。这些组件提供了现成的类,程序员只需调用这些类中的方法即可实现文件的上传与下载。本章将向读者介绍如何应用jspSmartUpload组件实现文件的上传与下载。
下载jspSmartUpload.zip,解压后得到的是一个Web应用程序,目录结构如图13.1 所示。
图13.1 jspSmartUpload.zip文件目录结构
default.htm为Web应用的首页面,sample1.htm~sample7.htm文件分别为7个实例中供用户选择上传文件和下载文件的静态页面,help目录下存放了jspSmartUpload组件的说明文件,jsp目录下存放了与sample1.htm~sample7.htm文件对应的JSP文件,用来实现当前实例中的动态内容,在这些JSP文件中将调用jspSmartUpload组件中的类来实现文件的上传或下载,Web-inf目录下存放的就是jspSmartUpload组件中的类文件。
若想运行该Web应用,首先将Web-inf目录名更改为WEB-INF,然后将jspsmartupload整个文件夹复制到Tomcat安装目录下的webapps目录下,最后访问http://localhost:8080/jspsmar tupload/default.htm地址即可进入Web应用的首页面,运行结果如图13.2所示。
图13.2 jspSmartUpload应用首页面
可以通过如下方法将Web-inf\classes目录下的文件打包成自己的JAR文件,以便在以后的程序开发时可以直接通过将该文件复制到应用的WEB-INF\lib目录下,来应用jspSmartUpload组件实现文件的上传与下载。
(1)若JDK安装在E:\JDK目录下,则环境变量的系统变量中应存在如下的配置:
JAVA_HOME=E:\JDK
PATH=%JAVA_HOME%\bin
(2)打开“命令提示符”窗口,进入classes目录下,输入如下的命令行进行文件打包:
jar cvf MyjspSmartUpload.jar com servletUpload.class servletUpload.java
com为classes目录下的com文件夹,MyjspSmartUpload.jsp文件即为打包后的文件。
在jspSmartUpload组件中主要包含了File、Files、Request和SmartUpload核心类,下面对这些核心类进行介绍。
该类不同于java.io.File类,在编写程序时应注意使用。File类用于保存单个上传文件的相关信息。如上传文件的文件名、文件大小、文件数据等,表13.1列出了File类中的常用方法。
表13.1 File类中的常用方法
方 法 |
说 明 |
saveAs() |
用于保存文件 |
isMissing() |
用于判断用户是否选择了文件,即表单中对应的<input type="file">标记实现的文件选择域中是否有值。该方法返回boolean型值,选择了文件时,返回False;否则返回True |
getFieldName() |
获取Form表单中当前上传文件所对应的表单项的名称 |
getFileName() |
获取文件的文件名,该文件名不包含目录 |
getFilePathName() |
获取文件的文件全名,获取的值是一个包含目录的完整文件名 |
getFileExt() |
获取文件的扩展名,即后缀名,不包含“.”符号 |
getContentType() |
获取文件MIME类型,如“text/plain” |
getContentString() |
获取文件的内容,返回值为String型 |
getSize() |
获取文件的大小、单位字节,返回值为int型 |
getBinaryData(int index) |
返回值为byte,获取文件数据中参数index指定位置处的一个字节,用于检测文件等处理 |
Files类中的saveAs()方法用于保存文件,在File类中提供了两种形式的saveAs()方法:
ll saveAs(String destFilePathName)方法。
ll saveAs(String destFilePathName, int optionSaveAs)方法。
这两个方法没有返回值,第一种形式与saveAs(destFilePathName, 0)执行效果相同。
参数说明
l destFilePathName:指定文件保存的路径,包括文件名。其值应以“/”开头。
l optionSaveAs:保存目标选项。该选项有3个值,分别是SAVEAS_AUTO、SAVEAS_ VIRTUAL和SAVEAS_PHYSICAL。它们是File类中的静态字段,分别表示整数0、1和2。将optionSaveAs参数设为SAVEAS_VIRTUAL选项值,则通知jspSmartUpload组件以Web应用的根目录为文件根目录,然后加上destFilePathName参数指定的路径来保存文件;设为SAVEAS_PHYSICAL值,则一种情况是通知jspSmartUpload组件将以Web服务器的安装路径中的磁盘根目录为文件根目录,然后加上destFilePathName参数指定的路径来保存文件,另一种情况则以destFilePathName参数指定的目录为最终目录来保存文件;设为SAVEAS_AUTO值,则首先以SAVEAS_VIRTUAL方式来保存文件,若Web应用下由destFilePathName参数指定的路径不存在,则以SAVEAS_PHYSICAL方式保存文件。
例如,若Web服务器(以Tomcat为例)的安装目录为“E:\Tomcat 5.5”,当前Web应用为“FileUpDown”,下面分别应用这3个选项保存文件。
(1)使用SAVEAS_VIRTUAL选项值。
saveAs("/file/myfile.txt",File.SAVEAS_VIRTUAL)或saveAs("/file/myfile.txt",1)
若FileUpDown应用下存在“file”子目录,则将上传的文件以“myfile.txt”为文件名进行保存,实际的保存路径如下:
E:\Tomcat 5.5\webapps\FileUpDown\file\myfile.txt
若不存在“file”子目录,则抛出下面的异常:
This path does not exist (1135)
(2)使用SAVEAS_PHYSICAL选项值。
saveAs("/file/myfile.txt",File.SAVEAS_PHYSICAL)或saveAs("/file/myfile.txt",2)
因为Tomcat 5.5安装在E盘,因此若E盘根目录下存在“file”子目录,则将上传的文件以“myfile.txt”为文件名进行保存,实际的保存路径如下:
E:\file\myfile.txt
若E盘根目录下不存在“file”子目录,而FileUpDown应用的根目录下存在“file”子目录,则抛出下面的异常:
The path is not a physical path
否则抛出下面的异常:
This path does not exist (1135)
使用SAVEAS_PHYSICAL选项值时,可以将上传的文件保存到由destFilePathName参数指定的一个具体的目录下,例如:
saveAs("D:/temp/myfile.txt",File.SAVEAS_PHYSICAL)
最终文件的实际保存路径如下:
D:\temp\myfile.txt
(3)使用SAVEAS_AUTO选项值。
saveAs("/file/myfile.txt",File.SAVEAS_AUTO)或saveAs("/file/myfile.txt",0)
若FileUpDown应用根目录下存在“file”子目录,则以SAVEAS_VIRTUAL方式保存文件,否则以SAVEAS_PHYSICAL方式保存文件。
通常情况下应使用SAVEAS_VIRTUAL方式保存文件,以便程序的移植。
该类存储了所有上传的文件,通过类中的方法可获得上传文件的数量和总长度等信息。表13.2列出了Files类中的常用方法。
表13.2 Files类中的常用方法
方 法 |
说 明 |
getCount() |
获取上传文件的数目,返回值为int型 |
getSize() |
获取上传文件的总长度,单位字节,返回值为long型 |
getFile(int index) |
获取参数index指定位置处的com.jspsmart.upload.File对象 |
getCollection() |
将所有File对象以Collection的形式返回 |
getEnumeration() |
将所有File对象以Enumeration形式返回 |
Files类中的getCollection()方法和getEnumeration()方法将所有的File对象分别以Collection和Enumeartion形式返回。
(1)getCollection()方法将所有File对象以Collection的形式返回,以便其他应用程序引用,该方法的具体代码如下:
public Collection getCollection(){
return m_files.values();
}
其中,“m_files”为Files类中的属性,其类型为Hashtable,它存储了所有的File对象。
(2)getEnumeration()方法将所有File对象以Enumeration形式返回,以便其他应用程序引用,该方法的具体代码如下:
public Enumeration getEnumeration(){
return m_files.elements();
}
其中,“m_files”为Files类中的属性,其类型为Hashtable,它存储了所有的File对象。
因为当Form表单用来实现文件上传时,通过JSP的内置对象request的getParameter()方法无法获取其他表单项的值,所以提供了该类来获取,Request类中提供了表13.3所示的方法。
表13.3 Request类中的常用方法
方 法 |
说 明 |
getParameter(String name) |
获取Form表单中由参数name指定的表单元素的值,如<input type="text" name="user">,当该表单元素不存在时,返回null |
getParameterNames() |
获取Form表单中除<input type="file">外的所有表单元素的名称,它返回一个枚举型对象 |
getParameterValues(String name) |
获取Form表单中多个具有相同名称的表单元素的值,该名称由参数name指定,该方法返回一个字符串数组 |
SmartUpload类用于实现文件的上传与下载操作,该类中提供的方法如下。
1.文件上传与文件下载必须实现的方法
在使用jspSmartUpload组件实现文件上传与下载时,必须先实现initialize()方法。在SmartUpload类中提供了该方法的3种形式。
l(1)initialize(ServletConfig config, HttpServletRequest request, HttpServletResponse response)。
l(2)initialize(ServletContext application, HttpSession session, HttpServletRequest request, HttpServletResponse response, JspWriter out)。
l(3)initialize(PageContext pageContext)。
通常应用第三种形式的方法,该方法中的pageContext参数为JSP的内置对象(页面上下文)。
2.文件上传使用的方法
实现文件上传,首先应实现initialize()方法,然后实现如下的两个方法即可将文件上传到服务器中。
l(1)upload()方法。
实现了initialize()方法后,紧接着就应实现该方法。upload()方法用来完成一些准备操作。首先在该方法中调用JSP的内置对象request的getInputStream()方法获取客户端的输入流,然后通过该输入流的read()方法读取用户上传的所有文件数据到字节数组中,然后在循环语句中从该字节数组中提取每个文件的数据,并将当前提取出来的文件的信息封装到File类对象中,最后将该File类对象通过Files类的addFile()方法添加到Files类对象中。
l(2)save()方法。
在实现了initialize()方法和upload()方法后,通过调用该方法就可以将全部上传文件保存到指定目录下,并返回保存的文件个数。该方法具有以下两种形式。
① save(String destPathName)。
② save(String destPathName, int option)。
第一个方法等同于save(destPathName,0)或save(destPathName,File. SAVE_AUTO)。
实际上在SmartUpload类的save()方法中最终是调用File类中的saveAs()方法保存文件的,所以save()方法中的参数使用与File类的saveAs()方法中的参数使用是相同的。但在save()方法中option参数指定的保存选项的可选值为SAVE_AUTO、SAVE_VIRTUAL和SAVE_PHYSICAL。它们是SmartUpload类中的静态字段,分别表示整数0、1和2。
仅仅通过以上的3个方法就实现了文件的上传。下面介绍SmartUpload类中可用来限制上传文件和获取其他信息的主要方法。
l(1)setDeniedFilesList(String deniedFilesList)方法。
该方法用于设置禁止上传的文件。其中参数deniedFilesList指定禁止上传文件的扩展名,多个扩展名之间以逗号分隔。若禁止上传没有扩展名的文件,以“,,”表示。例如,setDeniedFilesList("exe,jsp,,bat")表示禁止上传*.exe、*.jsp、*.bat和不带扩展名的文件。
l(2)setAllowedFilesList(String allowedFilesList)方法。
该方法用于设置允许上传的文件。其中参数allowedFilesList指定允许上传文件的扩展名,多个扩展名之间以逗号分隔。若允许上传没有扩展名的文件,以“,,”表示。例如,setAllowedFilesList("txt,doc,,")表示只允许上传*.txt、*.doc和不带扩展名的文件。
l(3)setMaxFileSize(long maxFileSize) ()方法。
该方法用于设定允许每个文件上传的最大长度,该长度由参数maxFileSize指定。
l(4)setTotalMaxFileSize(long totalMaxFileSize)()方法。
该方法用于设置允许上传文件的总长度,该长度由参数totalMaxFileSize指定。
上述的对上传文件进行限制的方法需要在upload()方法之前调用。下面为SmartUpload类中的获取文件信息的方法。
l(1)getSize()方法。
该方法用于获取上传文件的总长度,具体代码如下:
public int getSize(){
return m_totalBytes;
}
其中,m_totalBytes为SmartUpload类中的属性,表示上传文件的总长度,它是在upload()方法中通过调用JSP内置对象request的getContentLength()方法被赋值的。
l(2)getFiles()方法。
获取全部上传文件,以Files对象形式返回。
l(3)getRequest()方法。
获取com.jspsmart.upload.Request对象,然后通过该对象获得上传的表单中其他表单项的值。
3.文件下载使用的方法
l(1)setContentDisposition(String contentDisposition)方法。
该方法用于将数据追加到MIME文件头的CONTENT-DISPOSITION域。参数contentDisposition为要添加的数据。进行文件下载时,将contentDispotition设为null,则组件将自动添加“attachment”,表示将下载的文件作为附件,IE浏览器会弹出“文件下载”对话框,而不是自动打开这个文件(IE浏览器一般根据下载的文件扩展名决定执行什么操作,扩展名为doc的文件将用Word打开)。
l(2)downloadFile()方法。
downloadFile()方法实现文件下载,SmartUpload类中提供了4种形式的downloadFile()方法,如下所示。
① downloadFile(String sourceFilePathName) 。
② downloadFile(String sourceFilePathName, String contentType) 。
③ downloadFile(String sourceFilePathName, String contentType, String destFileName) 。
④ downloadFile(String sourceFilePathName, String contentType, String destFileName, int blockSize)。
downloadFile()方法中的参数及说明如表13.4所示。
表13.4 downloadFile()方法中的参数及说明
参 数 |
说 明 |
sourceFilePathName |
指定要下载文件的文件名(可带目录,如“/file/myfile.txt”或“E:/file/myfile.text”),若该文件名存在当前应用下,则sourceFilePathName=pageContext.getServletContext().getReal Path(sourceFilePathName) |
contentType |
指定一个文件内容类型(MIME格式的文件类型信息) |
destFileName |
指定下载的文件另存为的文件名 |
blockSize |
指定存储读取的文件数据的字节数组的大小,默认值为65000 |
通常使用第一种方法,如果需要更改文件的内容类型,或者更改下载文件另存为的文件名,或者更改用来存储读取的文件数据的字节数组的大小时,可应用后面的3种方法。
实例位置:光盘\mingrisoft\13\dxyy\01
运行本实例,将出现图13.3所示的界面,在该页中用户可选择要进行的操作。单击“文件上传”超级链接后进入fileup.jsp页面,如图13.4所示,在该页面中用户选择文件进行上传。单击“文件下载”超级链接后进入filedown.jsp页面,在该页面中将显示出可以下载的所有文件,可通过单击“下载”链接下载文件,如图13.5所示。
图13.4 文件上传界面 图13.5 文件下载界面
下面介绍程序的开发步骤。
在实例开发之前,通过在本章的13.1节中介绍的方法获得MyispSmartUpload.jar文件,然后将该文件复制到应用的Web-INF/lib目录下。
(1)创建首页面index.jsp,关键代码如下:
<table border="1">
<tr bgcolor="lightgrey"><td align="center">请选择文件操作</td></tr>
<tr>
<td align="center">
<a href="up/fileup.jsp">[文件上传]</a>
<a href="down/filedown.jsp">[文件下载]</a>
</td>
</tr>
</table>
(2)实现文件上传。首先创建提交上传文件的fileup.jsp页面,该文件包含一个Form表单,在进行文件上传时,表单的“method”属性必须为“post”,并且必须设置enctype="multipart/ form-data"属性,<input type="file">标记实现的表单元素可用来选择文件,通过上面的属性设置后,Form表单中通用的表单元素如<input type="text">,在表单提交后,通过JSP的request内置getParameter()方法将无法获取其值,可应用jspSmartUpload组件提供的Request类中的getParameter()方法获取。fileup.jsp页面的运行结果如图13.4所示。
fileup.jsp文件的关键代码如下:
<%
String errors=(String)request.getAttribute("errors");
if(errors==null||errors.equals(""))
errors="<li>请选择要上传的文件!</li>";
%>
<form action="doup.jsp" method="POST" enctype="multipart/form-data">
<table border="1">
<tr bgcolor="lightgrey"><td align="center" colspan="3"><%=errors%></td></tr>
<% for(int i=1;i<3;i++){ %>
<tr>
<td rowspan="2">文件<%=i%>:</td>
<td colspan="2"><input type="file" name="file<%=i%>"></td>
</tr>
<tr>
<td bgcolor="lightgrey">文件描述:</td>
<td><input type="text" name="info<%=i%>" maxlength="20"></td>
</tr>
<% } %>
<tr>
<td align="right" colspan="3">
<input type="submit" value="上传">
<input type="reset" value="重置">
<a href="../down/filedown.jsp">[文件下载]</a>
</td>
</tr>
</table>
</form>
然后创建接收表单、进行文件上传的doup.jsp文件,在该JSP文件中将调用jspSmartUpload组件进行文件上传。在doup.jsp页中不仅将文件上传到了服务器,并且将文件的信息存储到了数据库中,如文件的实际名称、上传到服务器进行存储的文件名和文件类型等。所以,这里不能直接调用SmartUpload类的save()方法将上传的所有文件存储到服务器中,而应逐个获取以jspSmartUpload组件中的File类表示的单个文件,然后获取当前文件的相应信息并存储在数据库中,最后调用Files类的saveAs()方法保存文件。doup.jsp文件的关键代码如下:
<%@ page import="com.jspsmart.upload.File" %>
<%@ page import="com.jspsmart.upload.Files" %>
<jsp:useBean id="myup" class="com.jspsmart.upload.SmartUpload"/>
<jsp:useBean id="mydb" class="com.fileUD.javabean.DB"/>
<center>正在上传文件,请稍等……</center>
<%
String filedir="/file/";
String errors="";
String sql="";
long maxsize=2*1024*1024; //设置每个上传文件的大小,为2MB
try{
myup.initialize(pageContext);
myup.setMaxFileSize(maxsize); //限制上传文件的大小
myup.upload(); //上传文件
Files files=myup.getFiles(); //获取所有的上传文件
for(int i=0;i<files.getCount();i++){ //逐个获取上传的文件
File singlefile=files.getFile(i);
if(!singlefile.isMissing()){ //如果选择了文件
String info=myup.getRequest().getParameter("info"+(i+1)); //获取通用
//表单元素的值
String type=singlefile.getContentType(); //获取文件类型
sql="insert into tb_file values('"+singlefile.getFileName()
+"','','"+type+"','"+info+"')";
int num=mydb.CUD(sql); //保存当前文件信息
if(num<=0){
errors+="<li>文件"+(i+1)+"上传失败:请检查是否输入了非法字符!</li>";
}
else{ //获取刚刚保存的文件信息记录的ID值,然后与文件的后缀名组成一个新的文件名
sql="select MAX(id) as maxid from tb_file";
java.sql.ResultSet rs=mydb.Read(sql);
String destname="";
if(rs.next()){
int maxid=rs.getInt("maxid");
destname=maxid+"."+singlefile.getFileExt();
sql="update tb_file set file_save='"+destname+"' where id="+maxid;
num=mydb.CUD(sql);
if(num<=0){
errors+="<li>文件"+(i+1)+"上传失败!</li>";
}
else{ //保存上传的文件
singlefile.saveAs(filedir+destname,File.SAVEAS_VIRTUAL);
errors+="<li>文件"+(i+1)+"上传成功!</li>";
}
}//if(rs.next())
}//else
}//if(!singlefile.isMissing())
}//for
request.setAttribute("errors",errors);
}catch(Exception e){
request.setAttribute("errors","文件上传失败!");
e.printStackTrace();
}
%>
<jsp:forward page="fileup.jsp"/>
代码中调用了DB类对数据库进行操作,具体代码可查看本书附带的光盘。
(3)实现文件下载。首先创建显示已上传文件的filedown.jsp页面,在该页中,会调用com.fileUD.javabean.DB类来查询已上传文件的信息,接着通过JSP中的Scriptlet进行显示。用户可通过单击“下载”超级链接进入dodown.jsp页面,在该页面中将调用jspSmartUpload组件进行文件下载。filedown.jsp页面的运行结果如图13.5所示。
filedown.jsp文件的关键代码如下:
<%@ page import="java.sql.ResultSet" %>
<jsp:useBean id="mydb" class="com.fileUD.javabean.DB"/>
<%
String errors=(String)request.getAttribute("errors");
if(errors==null||errors.equals(""))
errors="<li>请选择要下载的文件!";
String sql="select * from tb_file";
ResultSet rs=mydb.Read(sql);
%>
<table border="1">
<tr bgcolor="lightgrey"><td align="center" colspan="3"><%=errors%></td></tr>
<tr>
<td align="center">文件名</td>
<td align="center">文件描述</td>
<td align="center">下载</td>
</tr>
<%
if(rs.next()){
rs.previous();
while(rs.next()){
String filename=rs.getString("file_name");
String filesave=rs.getString("file_save");
String fileinfo=rs.getString("file_info");
%>
<tr>
<td align="center"><%=filename%></td>
<td align="center"><%=fileinfo%></td>
<td align="center">
<a href="dodown.jsp?downfile=<%=filesave%>">[下载]</a>
</tr>
<%
}
}
else{
out.println("<tr><td align='center' colspan='3'>没有文件显示!</td></tr>");
}
%>
<tr><td colspan="3"><a href="../up/fileup.jsp">[文件上传]</a></td></tr>
</table>
然后创建触发“文件下载”超级链接进入的用来实现下载的dodown.jsp页面。在该页面中首先通过JSP中的request内置对象获取请求中传递的downfile参数,它表示要下载的文件存储在服务器中所使用的文件名。
dodown.jsp页面的具体代码如下:
<jsp:useBean id="mydown" class="com.jspsmart.upload.SmartUpload"/>
<%
String downfile="/file/"+request.getParameter("downfile");
try{
response.reset();
out.clear();
out=pageContext.pushBody();
mydown.initialize(pageContext);
mydown.setContentDisposition(null);
mydown.downloadFile(downfile);
}catch(Exception e){
String errors="<li>文件下载失败:请检查选择的文件是否存在?</li>";
request.setAttribute("errors",errors);
RequestDispatcher rd=request.getRequestDispatcher("filedown.jsp");
rd.forward(request,response);
}
%>
在SmartUpload类用于下载文件的downloadFile()方法中,最终通过response.getOutput_ Stream().write()方法将要下载的文件数据写入到响应客户的输出流提供给用户进行下载,所以还要编写如下代码:
response.reset();
out.clear();
out=pageContext.pushBody();
否则,将抛出下面的异常:
getOutputStream() has already been called for this response