实例:JSP文件上传下载管理
在这个程序中,将利用流上传文件,利用基于对象流的文件操作保存文件列表,并将文件定向到客户端完成下载。
表单页面提交上传文件。文件以流的格式传输到服务器上,并被保存为一个临时文件。这个临时文件不仅包含上传文件的内容,还包含了文件信息相关的报头。文件上传程序需要分析报头,从中提出有用的文件信息。将文件信息保存在一个支持序列化借口的Java类中,并以对象流的方式写入文件。上传的文件内容也从临时文件中提出,保存在文件系统中。页面同时也以对象流的方式读取保存了上传列表的内容,将文件相关信息回复到Java类中,然后显示在界面上。
为了完成下载过程,程序以流的方式读取文件内容,并将其重定向到response对象的输出流上。同时文件相关信息也以HTTP报头的形式发送给客户端。客户端能根据HTTP报头得到文件相关信息,弹出下载对话框。
1.序列化Java类FileItem
首先是设计一个序列化的Java类存储必要的文件信息,例如文件名,文件路径,上传时间,文件大小,文件类型等。
1 package samples.javabean; 2 3 import java.io.Serializable; 4 import java.sql.Timestamp; 5 6 public class FileItem implements Serializable { 7 String name; 8 Integer length; 9 String path; 10 String contentType; 11 Timestamp createAt; 12 public FileItem(){ 13 this.name="default user"; 14 this.length=new Integer(0); 15 this.path="--"; 16 this.createAt=new Timestamp(System.currentTimeMillis()); 17 } 18 public String getName() { 19 return name; 20 } 21 public void setName(String name) { 22 this.name = name; 23 } 24 public Integer getLength() { 25 return length; 26 } 27 public void setLength(Integer length) { 28 this.length = length; 29 } 30 public String getPath() { 31 return path; 32 } 33 public void setPath(String path) { 34 this.path = path; 35 } 36 public String getContentType() { 37 return contentType; 38 } 39 public void setContentType(String contentType) { 40 this.contentType = contentType; 41 } 42 public Timestamp getCreateAt() { 43 return createAt; 44 } 45 public void setCreateAt(Timestamp createAt) { 46 this.createAt = createAt; 47 } 48 49 50 }
2.上传处理程序
上面的FIleItem类支持了序列化的接口,可以通过对象流进行读写。
本例子中将FileItem类存入到一个ArrayList类对象,并存储在文件中filelist.log.
下面的代码是页面部分。提供了一个文件上传表单。表单的input控件为file类型。表单的action指向了处理上传文件的Ex6_11_1.jsp。同时页面读取filelist.log文件,以基于对象流的方式读取保存文件列表的ArrayList,并将其存储的每个FileItem对象的信息现实在页面上。
1 <%@page import="com.sun.org.apache.bcel.internal.generic.NEWARRAY"%> 2 <%@ page language="java" contentType="text/html; charset=GB18030" 3 pageEncoding="GB18030"%> 4 <%@ page import="java.io.*" %> 5 <%@ page import="java.util.*" %> 6 <%@ page import="samples.javabean.*" %> 7 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 8 <html> 9 <head> 10 <meta http-equiv="Content-Type" content="text/html; charset=GB18030"> 11 <title>文件上传下载管理</title> 12 <% 13 File file=new File("/home/jim/Development/apache-tomcat-7.0.30/webapps/ROOT/filelist.log"); 14 if(!file.exists()){ 15 file.createNewFile(); 16 ObjectOutputStream newfile=new ObjectOutputStream( 17 new FileOutputStream(file)); 18 ArrayList list=new ArrayList(); 19 newfile.writeObject(list); 20 newfile.flush(); 21 newfile.close(); 22 } 23 %> 24 </head> 25 <body> 26 <font size="2"> 27 <form name="upload" method="post" action="Ex6_11_1.jsp" 28 enctype="multipart/form-data"> 29 上传文件<input type="file" name="file"> 30 <input type="submit" name="提交" value="Submit"> 31 </form> 32 <% 33 ObjectInputStream o=new ObjectInputStream( 34 new FileInputStream(file)); 35 ArrayList list=(ArrayList)o.readObject(); 36 o.close(); 37 if(list==null) list=new ArrayList(); 38 for(Iterator it=list.iterator();it.hasNext();){ 39 FileItem item=(FileItem)it.next(); 40 out.print(item.getName()+":"); 41 out.print(" "+item.getLength()+"字节"); 42 out.print(" 上传时间("+item.getCreateAt()+ 43 ")"); 44 out.print("<a href=\"Ex6_11_2.jsp?filename="+item.getName() 45 +"&Type="+item.getContentType()+"\">下载</a><br>"); 46 } 47 %> 48 </font> 49 50 </body> 51 </html>
单击确定按钮,表单将内容传送到action指向的程序。文件内容也包含在其中。可以使用request.getInputStream()方法获取输入流。然后将流的内容保存到一个文件中。但是这样的文件的内容并不完全是客户端选择的文件的内容.而是如图所示:
许多额外内容被保存到文件中,例如Content-Disposition域和Content-Type域。这些额外的信息可以称为报头。是HTTP协议通信时附加的。报头包含了一些有用的文件信息。因此,为了完成文件上传功能,必须分析这个包含报头的文件,取出里面的与文件相关的信息和文件内容。
文件的第二行的最后一部分包含了文件名。第三行包含了文件类型,第四行到倒数第六行是文件的内容。
下面的代码显示了如何具体街区报头中的信息:
1 <%@ page language="java" contentType="text/html; charset=GB18030" 2 pageEncoding="GB18030"%> 3 <%@ page import="java.io.*" %> 4 <%@ page import="java.util.*" %> 5 <%@ page import="samples.javabean.*" %> 6 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 7 <html> 8 <head> 9 <meta http-equiv="Content-Type" content="text/html; charset=GB18030"> 10 <title>Insert title here</title> 11 </head> 12 <body> 13 <% 14 try{ 15 File filelist=new File("/home/jim/Development/apache-tomcat-7.0.30/webapps/ROOT/filelist.log"); 16 File tempfile=new File("/home/jim/Development/apache-tomcat-7.0.30/webapps/ROOT/"+session.getId()); 17 //将表单所有内容存入临时文件 18 int buffersize=4096; 19 byte buffer[]=new byte[buffersize]; 20 FileOutputStream o =new FileOutputStream(tempfile); 21 request.setCharacterEncoding("GB18030"); 22 InputStream in=request.getInputStream(); 23 int count; 24 while((count=in.read(buffer))!=-1){ 25 o.write(buffer, 0, count); 26 } 27 o.close(); 28 in.close(); 29 //从包含表单数据的临时文件中提取文件信息 30 RandomAccessFile random=new RandomAccessFile(tempfile,"r"); 31 //第2行是Content-Disposition,其中最后一项是文件名 32 int linenum=1; 33 String fileheader=null; 34 while(linenum<=2){ 35 fileheader=random.readLine(); 36 linenum++; 37 } 38 int pos=fileheader.lastIndexOf('='); 39 String filename=fileheader.substring(pos+2, fileheader.length()-1); 40 //第三行是文件类型 41 String contenttype=random.readLine(); 42 pos=contenttype.lastIndexOf(':'); 43 contenttype=contenttype.substring(pos+1); 44 //第四行至倒数第六行是文件实际内容 45 //先找出第四行的文件指针位置 46 linenum=1; 47 random.seek(0); 48 int newline; 49 long offset_start=0,offset_end=0; 50 while((newline=random.readByte())!=-1&&linenum<=4){ 51 if(newline=='\n'){ 52 offset_start=random.getFilePointer(); 53 linenum++; 54 } 55 } 56 //找出倒数第六行文件指针位置 57 File uploaddir=new File("/home/jim/Development/apache-tomcat-7.0.30/webapps/ROOT/upload"); 58 if(!uploaddir.exists()) uploaddir.mkdir(); 59 File newfile=new File(uploaddir,filename); 60 RandomAccessFile random2=new RandomAccessFile(newfile,"rw"); 61 random.seek(random.length()); 62 offset_end=random.getFilePointer(); 63 long fp=offset_end; 64 linenum=1; 65 while(fp>=0&&linenum<=6){ 66 fp--; 67 random.seek(fp); 68 newline=random.readByte(); 69 if(newline=='\n'){ 70 offset_end=random.getFilePointer()-1; 71 linenum++; 72 } 73 } 74 random.seek(offset_start); 75 //将临时文件中文件实际内容存储到新的文件 76 long start=random.getFilePointer(); 77 while(start<offset_end-1){ 78 byte b=random.readByte(); 79 random2.write(b); 80 start=random.getFilePointer(); 81 } 82 //记录存储的文件,以对象的方式保存到文件中 83 FileItem item=new FileItem(); 84 item.setName(filename); 85 item.setLength(new Integer((int)random.length())); 86 item.setPath(newfile.getPath()); 87 item.setContentType(contenttype); 88 ObjectInputStream o1=new ObjectInputStream(new FileInputStream(filelist)); 89 ArrayList list=(ArrayList)o1.readObject(); 90 o1.close(); 91 if(list==null) list=new ArrayList(); 92 list.add(item); 93 ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream(filelist)); 94 output.writeObject(list); 95 output.flush(); 96 output.close(); 97 random2.close(); 98 random.close(); 99 response.sendRedirect("Ex6_11.jsp"); 100 }catch(IOException e){} 101 %> 102 </body> 103 </html>
程序运行结果为:
3.文件下载程序
接下来是实现文件下载功能。最简单的方法是直接给出文件的URL。但这样需要把文件暴露在WEb服务器的路径下。而且会暴露文件的实际路径信息。更好的方法是采用流读取文件,重定向到response对象的输出流。下面是具体的代码:
1 <%@ page language="java" contentType="text/html; charset=GB18030" 2 pageEncoding="GB18030"%> 3 <%@ page import="java.io.*" %> 4 <%@ page import="java.util.*" %> 5 <%@ page import="samples.javabean.*" %> 6 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 7 <html> 8 <head> 9 <meta http-equiv="Content-Type" content="text/html; charset=GB18030"> 10 <title>Insert title here</title> 11 </head> 12 <body> 13 <font size="2"> 14 <% 15 request.setCharacterEncoding("GB18030"); 16 String filename=request.getParameter("filename"); 17 String contenttype=request.getParameter("Type"); 18 OutputStream o=response.getOutputStream(); 19 int buffersize=4096; 20 byte[] buffer=new byte[buffersize]; 21 File uploaddir=new File("/home/jim/Development/apache-tomcat-7.0.30/webapps/ROOT/upload/"); 22 File download=new File(uploaddir,filename); 23 //设置Content-Disposition 24 response.setHeader("Content-Disposition", "attachment;filename="+filename); 25 //设置文件类型 26 response.setContentType(contenttype); 27 //设置文件大小 28 response.setHeader("Content_Length", download.length()+""); 29 //读取文件流写入输出 30 FileInputStream in =new FileInputStream(download); 31 int count; 32 while((count=in.read(buffer))!=-1){ 33 o.write(buffer,0,count); 34 } 35 in.close(); 36 o.close(); 37 %> 38 </font> 39 </body> 40 </html>
单击下载,显示如下结果: