JSP上传问题的研究
原文时间:2007-10-11[版权:badboy.net.cn]
因为是初学者,所以在我需要JSP为我提供上传功能的时候,我应该去了解一下实现细节,当我了解足够的时候,去使用别人的组件,或者给别人提供组件。
我首先是了解一下能用request.getInputStream()得到的数据是什么样子的:
1、新建一个HTML页面,含以下表单,用于选择和提交要上传的文件:
<form method="post" action="upload.jsp" ENCTYPE="multipart/form-data"><input type="file" name="file1" /><input type="submit" value="submit" /></form>
2、
新建一个JSP页面upload.jsp:
<%@ page contentType="text/html;charset=UTF-8"%><%@ page import="java.io.*"%><%try{ File file = new File("e:/upfile.txt"); InputStream is = request.getInputStream(); FileOutputStream fos = new FileOutputStream(file); int rn; byte[] buf = new byte; while((rn=is.read(buf,0,1024))!=-1) fos.write(buf,0,rn);is.close(); fos.close();}catch(Exception e){ }%>
这样,打开HTML页面选择文件并提交后,如无意外E:/下会多出一个upfile.txt文件,它的内容为:
清单1
1-----------------------------7d65d38307d22Content-Disposition: form-data; name="body3"; filename="C:\Documents and Settings\badboy\桌面\复件 (3) 新建 文本文档.txt"3Content-Type: text/plain45dasfdf6sdf7asdf8-----------------------------7d65d38307d2--
可以了解到:
1、1个文件被2个“-----------------------------7d65d38307d2”夹着
2、第2行到第4行是文件描述
3、文件内容由第5行开始,直到出现“-----------------------------7d65d38307d2”
4、第一行和最后一行的字符串很像,都是“-----------------------------7d65d38307d2”,最后一行多了“--”
因为考虑到上传的可能不是文本文件,但文件描述和分割线却是文本,所以全部用字符串读写方法分析应该行不通,受一本JSP教程的例子启发,决定用RandomAccessFile
RandomAccessFile 可以在文件内的任何位置随意跳转,并且可以读写字符串和字节,因而可以在以字符串方式分析到文件内容的起始位置和结束位置后很方便的跳转到适当位置再以字节方式读取和生成文件。
以下是一个例子,新建一个JSP文件,内容为
<%@ page contentType="text/html;charset=UTF-8"%><%@ page import="java.io.*"%><%try{File fl1 = new File("e:/upfile.txt");//这个是刚才那个文件aFile fl2 = new File("e:/upfile2.txt");//这个将是内容跟上传的那个文件一模一样的文件bRandomAccessFile raf1 = new RandomAccessFile(fl1,"r");//只读文件a 的 RandomAccessFile对象用来在upfile.txt里读出数据用来分析和生成文件RandomAccessFile raf2 = new RandomAccessFile(fl2,"rw");//这个是用于生成目的文件的RandomAccessFile对象String StringStart = null;//upfile.txt的第一行(“-----------------------------7d65d38307d2”) 是分割符String tmpStr = null;//要来临时存放一些字符串long startPos = 0 ;//实际文件内容的起始位置long endPos = 0;//实际文件内容的结束位置StringStart = raf1.readLine();//读取第一行即分割符raf1.readLine();raf1.readLine();raf1.readLine();//连续4次调用readLine()方法,使文件指针跳到了清单1 的第4行的开始位置,即实际文件的起始位置startPos = raf1.getFilePointer();//获得实际文件内容的起始位置//以下这个while循环在找到下一个类似“-----------------------------7d65d38307d2”的时候分割符退出,并且会计算实际文件内容的结束位置while((tmpStr=raf1.readLine())!=null){ if(tmpStr.indexOf(StringStart)!=-1) {out.print("found.");endPos = raf1.getFilePointer() - tmpStr.getBytes().length - 2;//获得实际文件内容的结束位置break; }}raf1.seek(startPos);//将指针重新定位到实际文件内容的起始位置现在已经知道实在文件内容在哪里开始哪里结束了//以下这个while循环将读取实际文件内容的起始位置到结束位置间的内容到upfile2.txt中理论上upfile2.txt的内容是上传的文件的内容一模一样的while(startPos<endPos){ raf2.write(raf1.readByte()); startPos = raf1.getFilePointer();}raf1.close();raf2.close();}catch(Exception e){}%>
上传一个文件的解决方法大概就是这样子,应该很好理解的,不过有几个问题:
1、怎样处理多个文件?
2、性能
关地第1个问题:
只要重复查找分割符并作一些类似的处理可以解决,以后有例子;
关于第2个问题:
我发现程序处理的时间大多是用于文件分析而不是文件的传输过程(我在本机测试传输过程用的时候可以忽略),而用于文件分析的时间我想大多是用在文件内部跳转的操作上,后来我用一个字节buffer读代替一个一个字节读和写速度有很大提高,但是处理5个共20M左右的MP3文件耗时比较多,这还是在本机上测试的。
以下是我做的一个JavaBean,我想大概不太符合某些规范,不过的确可以实现多文件上传:
下载地址:http://www.badboy.net.cn/badboy/bnc_fpr.rar
方法说明:http://www.badboy.net.cn/badboy/MethodSummary.html
实际上它只负责文件的分析.
以下是一个应用例子,一个包括了表单的JSP页面:
<%@ page contentType="text/html;charset=UTF-8"%><%@ page import="java.io.*"%><%@ page import="cn.net.badboy.FileParser"%><jsp:useBean id="fpr" scope="page" class="cn.net.badboy.FileParser" /><%String action=request.getParameter("action");if(action==null) action="";if(action.equals("save")){ String FilePath = request.getRealPath("/") + "uploadfiles";//设置上传文件的保存目录这里是jsp页面所在目录中的uploadfiles目录如果没有这个目录请手动为它新建一个 InputStream is = request.getInputStream(); File f = new File("e:/jt.bnc");//临时文件即包含分割符、文件描述和文件内容的那个文件 if(f.exists()) {f.delete();f = new File("e:/jt.bnc"); } FileOutputStream fos = new FileOutputStream(f); int rn; byte[] buf = new byte; while((rn=is.read(buf,0,1024))>0) {fos.write(buf,0,rn); }is.close(); fos.close();//到这里临时文件生成了余下的工作不再是从客户那里传输文件而是服务器方面的文件分析工作 fpr.parseFile(f,FilePath,"mp3,gif,jpg");//方法parseFile分析一个临时文件第一个参数指明临时文件对象第二个参数指明生成的文件的保存目录第三个参数指明充许的文件类型 out.println("Errors message :<br>"+fpr.getErrorMessage()+"<br>");//方法getErrorMessage返回文件分析过程中的错误例如有扩展名不合法的文件等 String[] FileNames = fpr.getFileNames();//方法getFileNames返回一个存放了成功上传的文件的文件名的字符串数组 out.println("file uploaded:<br>"); for(int i=0;i<FileNames.length;i++)out.println(FileNames+"<br>");}else{%><form method="post" action="?action=save"ENCTYPE="multipart/form-data"><input type="file" name="body1" /><input type="file" name="body2" /><input type="file" name="body3" /><input type="file" name="body4" /><input type="submit" value="submit" /></form><br /><%}%>
以下是bean cn.net.badboy.FileParser全部代码,有兴趣的朋友可以看看:
package cn.net.badboy;import java.io.*; /*don't remove this message .this bean from www.badboy.net.cn .please ! don't remove this message .all copyright reserved by www.badboy.net.cn .ar ... don't remove this message .*/public class FileParser{ private String[] FileNameArray = new String ; //this arraystores files' name that have been create . private int FileCount = 0 ; //tells how many file are there private String errorMessage = "" ; //***********************************private method definition begin******************************************************* //method setErrorMessage(String msg) private void setErrorMessage(String errorMessage) {this.errorMessage += "\n" + errorMessage ; } private boolean createFile(RandomAccessFile SourceFile,long startPos,long endPos,String FileDirectory,String FileName) {File file = null;RandomAccessFile targetFile = null;try{ file = new File(FileDirectory,FileName); if(file.exists()) //if the file exists,overwrite it. { file.delete(); file = new File(FileDirectory,FileName); } targetFile = new RandomAccessFile(file,"rw"); int rn; int bufferSize = 1024 ; byte[] buf = new byte; while(startPos<endPos) { if(startPos+bufferSize>=endPos) { targetFile.write(SourceFile.readByte()); startPos = SourceFile.getFilePointer(); } else { rn = SourceFile.read(buf,0,bufferSize); targetFile.write(buf,0,rn); startPos = SourceFile.getFilePointer(); } }}catch(Exception e){ e.printStackTrace(); return false ;}finally{ try{ if(targetFile!=null) { targetFile.close(); } } catch(IOException e) { e.printStackTrace(); return false; }}return true ; }private String getFileName(String NameInJerk,String allowableFiles) throws Exception {NameInJerk = new String(NameInJerk.getBytes("ISO-8859-1"),"UTF-8") ; //encoding to local charsetint startPos = NameInJerk.lastIndexOf("\\"); if(startPos==-1) return null ;String FileName = NameInJerk.substring(startPos+1,NameInJerk.length()-1);String[] TmpArr = FileName.split("\\.");String FileTypeName = TmpArr;if(allowableFiles.indexOf(FileTypeName)==-1){ setErrorMessage("file " +FileName + " is not a supported type of file to upload , upload failed ."); return null ;}return FileName; } private static void debug(String message) {System.out.println(message); //or log sth }//************************end private method definition******************************************* //*************************here begin the public method definition ****************************** //method getErrorMessage() //return the error message public String getErrorMessage() {return errorMessage ; }//public METHOD parseFile //parseFile接受参数TmpFileName(临时文件)解析临时文件,在FileDirectory(生成文件目录)生成约干文件 return true if success , or not false returned instead.public boolean parseFile(File TmpFile,String FileDirectory,String allowableFiles) {String CompartString = null ; //分割字符串通常为临时文件第一段内容String TmpString = null ; //临时字符串String FileName = null ;//保存文件名long startPos = 0 ; //每个文件内容的开始位置long endPos = 0 ; //每个文件内容的结束位置RandomAccessFile SourceFile = null ; try{ debug("starting......."); SourceFile = new RandomAccessFile(TmpFile,"r") ;//创建随机读取文件对象 CompartString = SourceFile.readLine(); //获得分割字符串 SourceFile.seek(0); //将操作位置重定位到文件开头 looper1: while(true) { while((TmpString=SourceFile.readLine())!=null){ if(TmpString.indexOf(CompartString)!=-1){ for(int i=1;i<=3;i++) { TmpString = SourceFile.readLine(); if(TmpString==null) break looper1; if(i==1) FileName= getFileName(TmpString,allowableFiles); //第二行包含文件名等信息可以获得文件名 } if(FileName!=null) break; } } debug("parsing..."); startPos = SourceFile.getFilePointer(); //获得文件内容的起始位置 while((TmpString=SourceFile.readLine())!=null) { if(TmpString.indexOf(CompartString)!=-1) { endPos = SourceFile.getFilePointer() - TmpString.getBytes().length - 2 ; //得到文件内容的结束位置 break; } } SourceFile.seek(startPos); //将操作位置重定位到startPos处开始生成一个文件 createFile(SourceFile,startPos,endPos,FileDirectory,FileName); //invoke生成文件方法生成文件 startPos = endPos + 2 ; debug("done..."); // add a file name to FileNameArray FileNameArray = FileName ; FileName = null ; } }catch(Exception e){ e.printStackTrace(); return false ;}finally{ try{ if(SourceFile!=null) { SourceFile.close(); TmpFile.delete(); //delete tmpfile maybe is should be deleted by who create it . } } catch(IOException e) { e.printStackTrace(); return false; }}return true; }//public METHOD getFileNames() //return an array storing some file name which was created . if it doesn't contains anything,the returned arrary length will be 0 . public String[] getFileNames() {String[] FileNameArray = new String;try{ for(int i=0;i<FileCount;i++) { FileNameArray = this.FileNameArray; }}catch(Exception e){ e.printStackTrace(); return FileNameArray;}return FileNameArray ; }public int getFileCount() {return FileCount; } //this operation may modify the file has been created . return false if no such file found or some error caused. //arguments should contains file directory . public boolean setFileName(String oldFileName,String newFileName){try{ File oldFile = new File(oldFileName); File newFile = new File(newFileName); oldFile.renameTo(newFile); oldFileName = oldFile.getName(); newFileName = newFile.getName(); for(int i=0;i<FileCount;i++) { if(FileNameArray.equals(oldFileName)) FileNameArray = newFileName; } }catch(Exception e){ e.printStackTrace(); return false ;}return true ; }}
我想性能的问题是出在指针的seek来seek去
希望同学们把自己的想法和大家分亨
页:
[1]