引用:
http://luoke920.javaeye.com/blog/271257
相关文章也可以看一下:
http://www.jspcn.net/htmlnews/2005011.html
http://www.jspcn.net/htmlnews/2005024.html
http://www.jspcn.net/htmlnews/20050115.html
一文中使用FileUpload组件实现多文件的上传。。。
经个人测试,组件本身使用上没有问题,不过因为考虑客户端和服务器有可能是linux主机的原因,在具体调用方面我做了一些改进。。。
本教程以Apache组织的commons项目中的FileUpload项目做为jsp的文件上传组件,FileUpload项目完全尊守RFC1867规范中
关于在HTTP request 中通过Post方法提交文件的规范,该项目性能稳定快速,易于部署和使用.
本次教程以前端jsp + 后端 servlet的方式上传文件,你也可以完全在jsp中实现而不用servlet.
在开始之前你要准备以下几个东西:
1. commons-FileUpload 1.2.1 包
下载地址:http://jakarta.apache.org/commons/fileupload/
具体地址:http://commons.apache.org/downloads/download_fileupload.cgi
下载Binary下的zip即可。
2. commons-IO 1.4 包
下载地址:http://jakarta.apache.org/commons/io/
具体地址:http://commons.apache.org/downloads/download_io.cgi
下载Binary下的zip即可。
3. Commons-BeanUtils 1.8 包
下载地址:http://jakarta.apache.org/commons/beanutils/
具体地址:http://commons.apache.org/downloads/download_beanutils.cgi
下载Binary下的zip即可。
好了下载完成后,解压得到5个jar包:分别是:
commons-beanutils-1.8.0.jar
commons-beanutils-bean-collections-1.8.0.jar
commons-beanutils-core-1.8.0.jar
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
配置好开发环境后:如果没配置好可以看:
http://blog.csdn.net/luweifeng1983/archive/2008/12/23/3590726.aspx
用的是Eclipse3.4 + tomcat5.5并已有tomcat插件一起的。(jdk1.3或1.5的都可,已测试)
新建一个项目名叫Upload
建立好后将类文件输出目录改为:Upload/WebContent/WEB-INF/classes
接下来将上面5个jar包拷贝到WEB-INF/lib目录。。
!!说到这里要注意一个问题,那就是lib下的jar文件和eclipse工程下的jar放置的区别,一般你要使用你的代码拷到别的电脑上也能运行就把所有的jar都先拷到lib下,然后在eclipse的build path下点add jars将lib中的jar加进来就行了。。
下面就分别建一个jsp和一个servlet。。
如下:upload.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Insert title here</title>
- </head>
- <body>
- <form name="upform" action="UploadServlet" method="POST" enctype="multipart/form-data">
- <input type ="file" name="file1" id="file1"/><br/>
- <input type ="file" name="file2" id="file2"/><br/>
- <input type ="file" name="file3" id="file3"/><br/>
- <input type="submit" value="Submit" /><br/>
- <input type="reset" />
- </form>
- </body>
- </html>
UploadServlet.java
- package com.gobusiness.eus.servlet;
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.fileupload.FileItemIterator;
- import org.apache.commons.fileupload.FileItemStream;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
- import org.apache.commons.fileupload.util.Streams;
- /**
- * Servlet implementation class for Servlet: UploadServlet
- *
- */
- public class UploadServlet extends javax.servlet.http.HttpServlet implements
- javax.servlet.Servlet {
- File tmpDir = null;// 初始化上传文件的临时存放目录
- File saveDir = null;// 初始化上传文件后的保存目录
- public UploadServlet() {
- super();
- }
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- doPost(request, response);
- }
- protected void doPost(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- try {
- if (ServletFileUpload.isMultipartContent(request)) {
- DiskFileItemFactory dff = new DiskFileItemFactory();// 创建该对象
- dff.setRepository(tmpDir);// 指定上传文件的临时目录
- dff.setSizeThreshold(1024000);// 指定在内存中缓存数据大小,单位为byte
- ServletFileUpload sfu = new ServletFileUpload(dff);// 创建该对象
- sfu.setFileSizeMax(5000000);// 指定单个上传文件的最大尺寸
- sfu.setSizeMax(10000000);// 指定一次上传多个文件的总尺寸
- FileItemIterator fii = sfu.getItemIterator(request);// 解析request
- // 请求,并返回FileItemIterator集合
- while (fii.hasNext()) {
- FileItemStream fis = fii.next();// 从集合中获得一个文件流
- if (!fis.isFormField() && fis.getName().length() > 0) {// 过滤掉表单中非文件域
- String fileName = fis.getName().substring(
- fis.getName().lastIndexOf("//"));// 获得上传文件的文件名
- BufferedInputStream in = new BufferedInputStream(fis
- .openStream());// 获得文件输入流
- BufferedOutputStream out = new BufferedOutputStream(
- new FileOutputStream(new File(saveDir
- + fileName)));// 获得文件输出流
- Streams.copy(in, out, true);// 开始把文件写到你指定的上传文件夹
- }
- }
- response.getWriter().println("File upload successfully!!!");// 终于成功了,还不到你的上传文件中看看,你要的东西都到齐了吗
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public void init() throws ServletException {
- /* 对上传文件夹和临时文件夹进行初始化 */
- super.init();
- String tmpPath = "c://tmpdir";
- String savePath = "c://updir";
- tmpDir = new File(tmpPath);
- saveDir = new File(savePath);
- if (!tmpDir.isDirectory())
- tmpDir.mkdir();
- if (!saveDir.isDirectory())
- saveDir.mkdir();
- }
- }
web.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
- <display-name>
- Upload</display-name>
- <servlet>
- <description>
- </description>
- <display-name>
- UploadServlet</display-name>
- <servlet-name>UploadServlet</servlet-name>
- <servlet-class>
- com.gobusiness.eus.servlet.UploadServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>UploadServlet</servlet-name>
- <url-pattern>/UploadServlet</url-pattern>
- </servlet-mapping>
- <welcome-file-list>
- <welcome-file>index.html</welcome-file>
- <welcome-file>index.htm</welcome-file>
- <welcome-file>index.jsp</welcome-file>
- <welcome-file>default.html</welcome-file>
- <welcome-file>default.htm</welcome-file>
- <welcome-file>default.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
好了,文件建立好后,在eclipse下直接部署再启动Tomcat访问upload.jsp再上传文件(这里可以一次上传三个文件),当然可以使用js实现提交更多的文件上传。。。
当然你也可以把你的upload项目整个拷贝到tomcat的webapps目录下,启动tomcat.然后
打开IE浏览器在地址栏中输入http://localhost:8080/upload/upload.jsp
如:我要上传的文件位于:D:/import file下,文件名分别为:
CNIL0437.TXT
GIDL0437.TXT
MSGL0437.TXT
上传成功后,这三个文件将被上传到了c:/updir下面。。。,如果在c:/updir下面找到了这三个文件说明上传已经成功了。。
好了,上面的步骤其实http://luoke920.javaeye.com/blog/271257已经讲得详细了,只是我把自己的部署、测试加进去了而以。那么我现在要对它进行一些改进。。。
首先第一:
- # public void init() throws ServletException {
- # /* 对上传文件夹和临时文件夹进行初始化 */
- # super.init();
- # String tmpPath = "c://tmpdir";
- # String savePath = "c://updir";
- # tmpDir = new File(tmpPath);
- # saveDir = new File(savePath);
- # if (!tmpDir.isDirectory())
- # tmpDir.mkdir();
- # if (!saveDir.isDirectory())
- # saveDir.mkdir();
- #
- # }
即它创建tempdir和updir是在servlet的init方法里面创建的,我们知道init方法只在servlet第一次被加载的时候执行,那么第一次创建成功后,如果后来的程序对这个目录做了修改或删除,那么从些以后就再也不能创建这两个目录了。。。
所以对创建目录的工作应该放到post方法里面。。。
第二:我们应该注意到程序在服务器上创建目录然后把文件上传到这两个目录(先放到temdir再到updir),如果我的服务器是linux主机怎么办,能用?
- 1. String tmpPath = "c://tmpdir";
- 2. String savePath = "c://updir";
显然不能,所以创建目录时要使用File.separator.
不过如果实在不想用File.separaotr,至少也要用/updir,而不是使用//,因为//在linux下是肯定没用的。。
第三:下面语句用于获取上传文件的文件名,即D:/import file/CNIL0437.TXT,也就是说要获得CNIL0437.TXT
- String fileName = fis.getName().substring(
- fis.getName().lastIndexOf("//"));// 获得上传文件的文件名
但上面的语句实际上只针对window客户端才有用,因为实际上上面的语句如果客户端使用的是window系统,上传文件时fis.getName()返回的是D://import file//CNIL0437.TXT
而这里也有一个要注意的地方就是即使在windows的客户端得到的fileName也是/CNIL0437.TXT而不是CNIL0437.TXT,这里因为
举例:"abchang".substring(2) = "chang".也就是substring(n)实际上是取第n+1到最后的字符。。所以这里应该改为
- String fileName = fis.getName().substring(
- fis.getName().lastIndexOf("//")+1);// 获得上传文件的文件名,客户端为windows
- if(fis.getName().lastIndexOf("//") == -1){
- fileName = fis.getName().substring(
- fis.getName().lastIndexOf("/")+1);// 获得上传文件的文件名,客户端为linux或unix
- }
上面lastIndexOf当找不到“//”时默认返回值为-1
第四:下面语句
- BufferedOutputStream out = new BufferedOutputStream(
- new FileOutputStream(new File(saveDir
- + fileName)));// 获得文件输出流
上面saveDir是一个File的实例,而更奇怪的是这个程序确实可以运行,说实话我现在没有明白这个语句编译器没有报错:new File(saveDir + fileName)
因为File的构造函数中好象没有File(File+ String) 这么的构造函数。。这个问题??保留。。有看到这个问题并知道的可以解释一下。。。
- 上面提到的问题已解决了
- 这里使用的+号运算符实际上相当于saveDir.toString + String
jdk api:中只有new File(File,String)构造函数,经测试这里使用:new File(saveDir,fileName)也可以。
这里同样有使用File.separator的问题:这里因为上面取得的 fileName是/CNIL0437.TXT,而我改正后的是CNIL0437.TXT。。所以这里也必须改。。
改正后为:
- new FileOutputStream(new File(savePath + File.separator + fileName)));// 获得文件输出流
注意我改成了savePath 因为savePath 是String类型的。。
好了这个程序到这里改得基本差不多了。。。
- 注:
- 这个程序里,我的上传文件的目录名和文件名都用的是英文,但实际当中可能使用中文名称,特别是文件名是中文的情况,经测试程序当文件名和目录名中有中文时获得的是乱码,这个问题有待解决,此贴名称前面加“!”以示待改进!
- 2008-12-26:
- 修正bug:当上传的文件目录或文件名为中文时提交过后用fis.getName()得到的是乱码
- 如:C:/Documents and Settings/zqwf/桌面/华为面试题.txt
- 如果不做任何处理理到的将是:
- C:/Documents and Settings/zqwf/妗岄潰/鍗庝负闈㈣瘯棰�.txt
- 在网上看了一些有关上传文件时乱码的问题,也看了FileUpload组件的相关源代码,但后来认为不需要对源码做修改。。。。
- 再看我前面的一篇博文:http://blog.csdn.net/luweifeng1983/archive/2008/11/20/3342003.aspx
- 提到post提交的乱码处理方法直接在servlet中添加
- request.setCharacterEncoding("UTF-8");即可解决问题
下面看看我实际应用的环境:
服务器:jboss3.2.0
eclipse 1.3.1
服务器:windows和linux都有,正因为这个所以上面改进了一些地方,总结起来就是在new File的时候里面应该使用File.separator,但在外面普通字符串来写目录的时候一般不要使用。。。
另外一般把目录写成相对目录,然后保存在属性文件中,注意的是:服务器不同,这两个属性文件配置的目录也有些不同。。好了看我下面的实现
sys.properties文件
windows服务器
- path.install = c://GeTSmart
- path.web.app = ../jboss-3.2.0/server/minimal/deploy/GeTSmart.war/WEB-INF
- path.doc.ie.imp = ./csv
- path.doc.ie.imp.temp = ./tmpdir
- path.install =/usr/local/getseus
- path.web.app = ../jboss-3.2.1/server/minimal/deploy/GeTSmart.war/WEB-INF
- path.doc.ie.imp = ./csv
- path.doc.ie.imp.temp = ./tmpdir
- try {
- String tmpPath = EusUtil.getFullPath(SysProp.getProperty("path.doc.ie.imp.temp"));
- //String tmpPath = "c://tmpdir";
- String savePath = EusUtil.getFullPath(SysProp.getProperty("path.doc.ie.imp"));
- //String savePath = "c://updir";
- tmpDir = new File(tmpPath);
- saveDir = new File(savePath);
- if (!tmpDir.isDirectory())
- tmpDir.mkdir();
- if (!saveDir.isDirectory())
- saveDir.mkdir();
- Logger.logDebug(className,"the savePath dir name is :" + savePath);
- String userId = userBean.getUserId();
- savePath =savePath + File.separator + userId;
- saveDir = new File(savePath);
- if (!saveDir.isDirectory())
- saveDir.mkdir();
- Logger.logDebug(className,"the savePath dir and the userid name is :" + savePath);
- if (ServletFileUpload.isMultipartContent(req)) {
- DiskFileItemFactory dff = new DiskFileItemFactory();// 创建该对象
- dff.setRepository(tmpDir);// 指定上传文件的临时目录
- dff.setSizeThreshold(1024000);// 指定在内存中缓存数据大小,单位为byte
- ServletFileUpload sfu = new ServletFileUpload(dff);// 创建该对象
- sfu.setFileSizeMax(5000000);// 指定单个上传文件的最大尺寸
- sfu.setSizeMax(10000000);// 指定一次上传多个文件的总尺寸
- FileItemIterator fii = sfu.getItemIterator(req);// 解析request
- // 请求,并返回FileItemIterator集合
- while (fii.hasNext()) {
- FileItemStream fis = fii.next();// 从集合中获得一个文件流
- if (!fis.isFormField() && fis.getName().length() > 0) {// 过滤掉表单中非文件域
- String fileName = fis.getName().substring(
- fis.getName().lastIndexOf("//")+1);// 获得上传文件的文件名,客户端为windows
- if(fis.getName().lastIndexOf("//")<0){
- fileName = fis.getName().substring(
- fis.getName().lastIndexOf("/")+1);// 获得上传文件的文件名,客户端为linux或unix
- }
- //fis.getName().lastIndexOf(File.separator)+1);// 获得上传文件的文件名
- Logger.logDebug(className, "get the File.separator is :" + File.separator);
- Logger.logDebug(className, "get the client import file path is <<fis.getName()>>:" + fis.getName());
- Logger.logDebug(className, "get the client import file path last index of // + 1 is <<fis.getName() index of >>:" + (fis.getName().lastIndexOf("//")+1));
- Logger.logDebug(className, "get the import file name is :" + fileName);
- BufferedInputStream in = new BufferedInputStream(fis
- .openStream());// 获得文件输入流
- BufferedOutputStream out = new BufferedOutputStream(
- //new FileOutputStream(new File(saveDir + fileName)));// 获得文件输出流
- new FileOutputStream(new File(savePath + File.separator + fileName)));// 获得文件输出流
- Logger.logDebug(className, "get the outputStream file path is <<new File(savePath + File.separator + fileName)>>" + savePath + File.separator + fileName);
- Streams.copy(in, out, true);// 开始把文件写到你指定的上传文件夹
- }
- }
- //resp.getWriter().println("File upload successfully!!!");// 终于成功了,还不到你的上传文件中看看,你要的东西都到齐了吗
- public static String getFullPath(String path) {
- //get install path
- String sInstallPath=CommonUtil.null2String(SysProp.getProperty("path.install"));
- String sWebAppPath =CommonUtil.null2String(SysProp.getProperty("path.web.app"));
- if (path.trim().substring(0,2).equals("..")) {
- return replaceNameSeparator(sInstallPath+path.trim().substring(2));
- }
- else if (path.trim().charAt(0)=='.') {
- if (sWebAppPath.trim().substring(0,2).equals(".."))
- return replaceNameSeparator(sInstallPath+sWebAppPath.trim().substring(2)+path.trim().substring(1));
- else
- return replaceNameSeparator(sWebAppPath+path.trim().substring(1));
- } else {
- return replaceNameSeparator(path.trim());
- }
- }
- public static String replaceNameSeparator(String path)
- {
- String newString = "";
- if (path.indexOf('/')!=-1)
- newString = path.replace('/', File.separatorChar);
- else
- newString = path;
- if (newString.indexOf('//')!=-1)
- newString = newString.replace('//', File.separatorChar);
- return newString;
- }
说大了就是编程要注意细节,要尽量去完善代码。。。这样才能使代码不会因环境变化一下就不能运行。。。。
前面讲过可以实现更多文件上传,这里只需要改一下jsp页面就行了,改进后的代码如下:
upload.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Insert title here</title>
- <script>
- var file_to=0;
- function add_inp()
- {
- newInp = document.createElement("input");
- newInp.setAttribute("id","file_"+file_to);
- newInp.setAttribute("type","file");
- newInp.setAttribute("name","upfile");
- div1.appendChild(newInp);
- newInp = document.createElement('<input type="button" value="删除" οnclick="del_inp('+file_to+')">');
- newInp.setAttribute("id","file_del_"+file_to);
- div1.appendChild(newInp);
- newInp = document.createElement('<BR />');
- newInp.setAttribute("id","file_br_"+file_to);
- div1.appendChild(newInp);
- file_to++;
- }
- function del_inp(file_id)
- {
- div1.removeChild(document.getElementById("file_"+file_id));
- div1.removeChild(document.getElementById("file_del_"+file_id));
- div1.removeChild(document.getElementById("file_br_"+file_id));
- }
- </script>
- </head>
- <body>
- <form name="upform" action="UploadServlet" method="POST" enctype="multipart/form-data">
- <div id=div1>
- </div>
- <INPUT TYPE="button" VALUE="添加上传文件" οnclick=add_inp()>
- <input type="submit" value="Submit" />
- <input type="reset" />
- </form>
- </body>
- </html>
最近查看fileupload的api的时候发现有另一种迭代及写文件的方式如下:
- protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- String step = "";
- File tempDir = null;
- File saveDir = null;
- try{
- request.setCharacterEncoding("UTF-8");
- step = "upload file to c:/updir";
- String tempPath = "c:/tempdir";
- String savePath = "c:/updir";
- tempDir = ImportManager.mkdir(tempPath);
- saveDir = ImportManager.mkdir(savePath);
- int maxMemorySize = 1024 * 1024 * 1024;
- int MaxSize = 100 * 1024 * 1024;
- if(ServletFileUpload.isMultipartContent(request)){
- DiskFileItemFactory factory = new DiskFileItemFactory();
- factory.setSizeThreshold(maxMemorySize);
- factory.setRepository(tempDir);
- ServletFileUpload upload = new ServletFileUpload(factory);
- upload.setSizeMax(MaxSize);
- List items = upload.parseRequest(request);
- Iterator iter = items.iterator();
- while(iter.hasNext()){
- FileItem item = (FileItem)iter.next();
- if(!item.isFormField()){
- //item.isFormField()返回为ture时为其它的表单域如submit等,为false时才为上传的文件
- String fieldName = item.getFieldName();
- String value = item.getString();
- String fileName = item.getName();
- String contentType = item.getContentType();
- boolean isInMemory = item.isInMemory();
- long sizeInbytes = item.getSize();
- //取上传的文件名
- if(fileName.lastIndexOf("//") != -1){
- fileName = fileName.substring(fileName.lastIndexOf("//")+1);
- }else{
- fileName = fileName.substring(fileName.lastIndexOf("/")+1);
- }
- //保存文件到指定目录
- item.write(new File(saveDir + File.separator + fileName));
- // BufferedInputStream is = new BufferedInputStream(item.getInputStream());
- // BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(new File(savePath + File.separator + fileName)));
- // Streams.copy(is,os,true);
- }
- }
- }
- }catch(Exception e){
- }finally{
- request.getRequestDispatcher("AckMessage.jsp").forward(request, response);
- }
- }
parseRequest
,而前面的例子使用的是该类返回FileItemIterator
的
getItemIterator
方法