用java在web环境下上传和下载文件的技巧
文件上传在web应用中非常普遍,要在jsp环境中实现文件上传功能是非常容易的,因为网上有许多用java开发的文件上传组件,本文以commons-fileupload组件为例,为jsp应用添加文件上传功能。
common-fileupload组件是apache的一个开源项目之一,可以从http://jakarta.apache.org/commons/fileupload/下载。
用该组件可实现一次上传一个或多个文件,并可限制文件大小。
下载后解压zip包,将commons-fileupload-1.0.jar复制到tomcat的webapps你的webappWEB-INFlib下,目录不存在请自建目录。
新建一个servlet:Upload.java用于文件上传:
importjava.io.*;
importjava.util.*;
importjavax.servlet.*;
importjavax.servlet.http.*;
importorg.apache.commons.fileupload.*;
publicclassUploadextendsHttpServlet{
privateStringuploadPath="C:upload";//上传文件的目录
privateStringtempPath="C:uploadtmp";//临时文件目录
publicvoiddoPost(HttpServletRequestrequest,
HttpServletResponseresponse)
throwsIOException,ServletException
{
}
}
在doPost()方法中,当servlet收到浏览器发出的Post请求后,实现文件上传。以下是示例代码:
publicvoiddoPost(HttpServletRequestrequest,
HttpServletResponseresponse)
throwsIOException,ServletException
{
try{
DiskFileUploadfu=newDiskFileUpload();
//设置最大文件尺寸,这里是4MB
fu.setSizeMax(4194304);
//设置缓冲区大小,这里是4kb
fu.setSizeThreshold(4096);
//设置临时目录:
fu.setRepositoryPath(tempPath);
//得到所有的文件:
ListfileItems=fu.parseRequest(request);
Iteratori=fileItems.iterator();
//依次处理每一个文件:
while(i.hasNext()){
FileItemfi=(FileItem)i.next();
//获得文件名,这个文件名包括路径:
StringfileName=fi.getName();
//在这里可以记录用户和文件信息
//...
//写入文件,暂定文件名为a.txt,可以从fileName中提取文件名:
fi.write(newFile(uploadPath+"a.txt"));
}
}
catch(Exceptione){
//可以跳转出错页面
}
}
如果要在配置文件中读取指定的上传文件夹,可以在init()方法中执行:
publicvoidinit()throwsServletException{
uploadPath=....
tempPath=....
//文件夹不存在就自动创建:
if(!newFile(uploadPath).isDirectory())
newFile(uploadPath).mkdirs();
if(!newFile(tempPath).isDirectory())
newFile(tempPath).mkdirs();
}
编译该servlet,注意要指定classpath,确保包含commons-upload-1.0.jar和tomcatcommonlibservlet-api.jar。
配置servlet,用记事本打开tomcatwebapps你的webappWEB-INFweb.xml,没有的话新建一个。
典型配置如下:
〈?xmlversion="1.0"encoding="ISO-8859-1"?〉 〈!DOCTYPEweb-app PUBLIC"-//SunMicrosystems,Inc.//DTDWebApplication2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"〉 〈web-app〉 〈servlet〉 〈servlet-name〉Upload〈/servlet-name〉 〈servlet-class〉Upload〈/servlet-class〉 〈/servlet〉 〈servlet-mapping〉 〈servlet-name〉Upload〈/servlet-name〉 〈url-pattern〉/fileupload〈/url-pattern〉 〈/servlet-mapping〉 〈/web-app〉
配置好servlet后,启动tomcat,写一个简单的html测试:
〈formaction="fileupload"method="post" enctype="multipart/form-data"name="form1"〉 〈inputtype="file"name="file"〉 〈inputtype="submit"name="Submit"value="upload"〉 〈/form〉
注意action="fileupload"其中fileupload是配置servlet时指定的url-pattern。
下面是某个大虾的代码:
这个Upload比smartUpload好用多了.完全是我一个个byte调试出来的,不象smartUpload的bug具多.
调用方法:
Uploadup=newUpload(); up.init(request); /** 此处可以调用setSaveDir(StringsaveDir);设置保存路径 调用setMaxFileSize(longsize)设置上传文件的最大字节. 调用setTagFileName(String)设置上传后文件的名字(只对第一个文件有效) */ up.uploadFile();
然后String[]names=up.getFileName();得到上传的文件名,文件绝对路径应该是
保存的目录saveDir+"/"+names[i];
可以通过up.getParameter("field");得到上传的文本或up.getParameterValues("filed")
得到同名字段如多个checkBox的值.
其它的自己试试.
源码如下所示:____________________________________________________________
packagecom.inmsg.beans;
importjava.io.*;
importjava.util.*;
importjavax.servlet.*;
importjavax.servlet.http.*;
publicclassUpload{
privateStringsaveDir=".";//要保存文件的路径
privateStringcontentType="";//文档类型
privateStringcharset="";//字符集
privateArrayListtmpFileName=newArrayList();//临时存放文件名的数据结构
privateHashtableparameter=newHashtable();//存放参数名和值的数据结构
privateServletContextcontext;//程序上下文,用于初始化
privateHttpServletRequestrequest;//用于传入请求对象的实例
privateStringboundary="";//内存数据的分隔符
privateintlen=0;//每次从内在中实际读到的字节长度
privateStringqueryString;
privateintcount;//上载的文件总数
privateString[]fileName;//上载的文件名数组
privatelongmaxFileSize=1024*1024*10;//最大文件上载字节;
privateStringtagFileName="";
publicfinalvoidinit(HttpServletRequestrequest)throwsServletException{
this.request=request;
boundary=request.getContentType().substring(30);//得到内存中数据分界符
queryString=request.getQueryString();
}
publicStringgetParameter(Strings){//用于得到指定字段的参数值,重写request.getParameter(Strings)
if(parameter.isEmpty()){
returnnull;
}
return(String)parameter.get(s);
}
publicString[]getParameterValues(Strings){//用于得到指定同名字段的参数数组,重写request.getParameterValues(Strings)
ArrayListal=newArrayList();
if(parameter.isEmpty()){
returnnull;
}
Enumeratione=parameter.keys();
while(e.hasMoreElements()){
Stringkey=(String)e.nextElement();
if(-1!=key.indexOf(s+"||||||||||")||key.equals(s)){
al.add(parameter.get(key));
}
}
if(al.size()==0){
returnnull;
}
String[]value=newString[al.size()];
for(inti=0;i〈value.length;i++){
value[i]=(String)al.get(i);
}
returnvalue;
}
publicStringgetQueryString(){
returnqueryString;
}
publicintgetCount(){
returncount;
}
publicString[]getFileName(){
returnfileName;
}
publicvoidsetMaxFileSize(longsize){
maxFileSize=size;
}
publicvoidsetTagFileName(Stringfilename){
tagFileName=filename;
}
publicvoidsetSaveDir(StringsaveDir){//设置上载文件要保存的路径
this.saveDir=saveDir;
Filetestdir=newFile(saveDir);//为了保证目录存在,如果没有则新建该目录
if(!testdir.exists()){
testdir.mkdirs();
}
}
publicvoidsetCharset(Stringcharset){//设置字符集
this.charset=charset;
}
publicbooleanuploadFile()throwsServletException,IOException{//用户调用的上载方法
setCharset(request.getCharacterEncoding());
returnuploadFile(request.getInputStream());
}
privatebooleanuploadFile(ServletInputStreamservletinputstream)throws//取得央存数据的主方法
ServletException,IOException{
Stringline=null;
byte[]buffer=newbyte[256];
while((line=readLine(buffer,servletinputstream,charset))!=null){
if(line.startsWith("Content-Disposition:form-data;")){
inti=line.indexOf("filename=");
if(i〉=0){//如果一段分界符内的描述中有filename=,说明是文件的编码内容
StringfName=getFileName(line);
if(fName.equals("")){
continue;
}
if(count==0&&tagFileName.length()!=0){
Stringext=fName.substring((fName.lastIndexOf(".")+1));
fName=tagFileName+"."+ext;
}
tmpFileName.add(fName);
count++;
while((line=readLine(buffer,servletinputstream,charset))!=null){
if(line.length()〈=2){
break;
}
}
Filef=newFile(saveDir,fName);
FileOutputStreamdos=newFileOutputStream(f);
longsize=0l;
while((line=readLine(buffer,servletinputstream,null))!=null){
if(line.indexOf(boundary)!=-1){
break;
}
size+=len;
if(size〉maxFileSize){
thrownewIOException("文件超过"+maxFileSize+"字节!");
}
dos.write(buffer,0,len);
}
dos.close();
}
else{//否则是字段编码的内容
Stringkey=getKey(line);
Stringvalue="";
while((line=readLine(buffer,servletinputstream,charset))!=null){
if(line.length()〈=2){
break;
}
}
while((line=readLine(buffer,servletinputstream,charset))!=null){
if(line.indexOf(boundary)!=-1){
break;
}
value+=line;
}
put(key,value.trim(),parameter);
}
}
}
if(queryString!=null){
String[]each=split(queryString,"&");
for(intk=0;k〈each.length;k++){
String[]nv=split(each[k],"=");
if(nv.length==2){
put(nv[0],nv[1],parameter);
}
}
}
fileName=newString[tmpFileName.size()];
for(intk=0;k〈fileName.length;k++){
fileName[k]=(String)tmpFileName.get(k);//把ArrayList中临时文件名倒入数据中供用户调用
}
if(fileName.length==0){
returnfalse;//如果fileName数据为空说明没有上载任何文件
}
returntrue;
}
privatevoidput(Stringkey,Stringvalue,Hashtableht){
if(!ht.containsKey(key)){
ht.put(key,value);
}
else{//如果已经有了同名的KEY,就要把当前的key更名,同时要注意不能构成和KEY同名
try{
Thread.currentThread().sleep(1);//为了不在同一ms中产生两个相同的key
}
catch(Exceptione){}
key+="||||||||||"+System.currentTimeMillis();
ht.put(key,value);
}
}
/*
调用ServletInputstream.readLine(byte[]b,intoffset,length)方法,该方法是从ServletInputstream流中读一行
到指定的byte数组,为了保证能够容纳一行,该byte[]b不应该小于256,重写的readLine中,调用了一个成员变量len为
实际读到的字节数(有的行不满256),则在文件内容写入时应该从byte数组中写入这个len长度的字节而不是整个byte[]
的长度,但重写的这个方法返回的是String以便分析实际内容,不能返回len,所以把len设为成员变量,在每次读操作时
把实际长度赋给它.
也就是说在处理到文件的内容时数据既要以String形式返回以便分析开始和结束标记,又要同时以byte[]的形式写到文件
输出流中.
*/
privateStringreadLine(byte[]Linebyte,
ServletInputStreamservletinputstream,Stringcharset){
try{
len=servletinputstream.readLine(Linebyte,0,Linebyte.length);
if(len==-1){
returnnull;
}
if(charset==null){
returnnewString(Linebyte,0,len);
}
else{
returnnewString(Linebyte,0,len,charset);
}
}
catch(Exception_ex){
returnnull;
}
}
privateStringgetFileName(Stringline){//从描述字符串中分离出文件名
if(line==null){
return"";
}
inti=line.indexOf("filename=");
line=line.substring(i+9).trim();
i=line.lastIndexOf("");
if(i〈0||i〉=line.length()-1){
i=line.lastIndexOf("/");
if(line.equals("""")){
return"";
}
if(i〈0||i〉=line.length()-1){
returnline;
}
}
returnline.substring(i+1,line.length()-1);
}
privateStringgetKey(Stringline){//从描述字符串中分离出字段名
if(line==null){
return"";
}
inti=line.indexOf("name=");
line=line.substring(i+5).trim();
returnline.substring(1,line.length()-1);
}
publicstaticString[]split(StringstrOb,Stringmark){
if(strOb==null){
returnnull;
}
StringTokenizerst=newStringTokenizer(strOb,mark);
ArrayListtmp=newArrayList();
while(st.hasMoreTokens()){
tmp.add(st.nextToken());
}
String[]strArr=newString[tmp.size()];
for(inti=0;i〈tmp.size();i++){
strArr[i]=(String)tmp.get(i);
}
returnstrArr;
}
}
下载其实非常简单,只要如下处理,就不会发生问题。
publicvoiddownLoad(StringfilePath,HttpServletResponseresponse,booleanisOnLine)
throwsException{
Filef=newFile(filePath);
if(!f.exists()){
response.sendError(404,"Filenotfound!");
return;
}
BufferedInputStreambr=newBufferedInputStream(newFileInputStream(f));
byte[]buf=newbyte[1024];
intlen=0;
response.reset();//非常重要
if(isOnLine){//在线打开方式
URLu=newURL("file:///"+filePath);
response.setContentType(u.openConnection().getContentType());
response.setHeader("Content-Disposition","inline;filename="+f.getName());
//文件名应该编码成UTF-8
}
else{//纯下载方式
response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition","attachment;filename="+f.getName());
}
OutputStreamout=response.getOutputStream();
while((len=br.read(buf))〉0)
out.write(buf,0,len);
br.close();
out.close();
}