在近期的一個項目中有用到commons-fileUpload組件進行實現文件上傳的功能(由於沒用到框架),在使用的過程中有遇到一些問題,經過自己的琢磨也算順利地將其解決了,在這里做個記錄。
一、commons-fileUpload文件上傳組件簡介
commons-fileUpload上傳組件是Apache的一個開源項目,可以到http://commons.apache.org/proper/commons-fileupload/下載最新版本(該組件需要commons-io包的支持)。該組件使用方便,同樣可以實現一個或多個文件的上傳,也可實現限制上傳文件大小等功能。
在文件上傳中,文件上傳請求由有序的表單項列表組成,fileUpload能夠解析上傳請求,然后向應用提供一個單獨的文件表單項的列表。每個這樣的表單項都實現了FileItem接口,並包含例如文件名等屬性。
二、使用要求
1、使用commons-fileUpload組件上傳文件時,需要將form表單的enctype屬性設置為multipart/form-data;同時還需要設置上傳文件在內存中的大小,多余的部分存儲在磁盤中。
2、將相應的commons-fileUpload包和commons-io包拷貝放入到你的web工程WEN-INF/lib目錄下並Add to Build Path。
三、使用步驟
1、首先,創建磁盤工廠DiskFileItemFactory對象,用來配置上傳組件ServletFileUpload;
DiskFileItemFactory factory = new DiskFileItemFactory();
DiskFileItemFactory類的常用方法
方法 | 返回值 | 描述 |
setSizeThrehold() | void | 該方法需要傳入一個int型參數,用來設置最大內存大小(byte為單位) |
setRepositoryPath() | void |
該方法需要傳入一個String型參數,用來設置臨時文件目錄 |
getRepository() | File | 獲取保存臨時文件地址 |
2、其次,創建ServletFileUpload實例,即創建上傳文件的句柄。
可通過DiskFileItemFactory實例構造ServletFileUpload對象,代碼如下:
ServletFileUpload upload = newServletFileUpload(factory);
方法 | 返回值 | 描述 |
isMultipartContent() | boolean | 檢查是否是一個文件上傳請求 |
parseRequest() | List | 從request對象中得到所有上傳域表單 |
四、使用例子(servlet部分代碼)
下面就給出一個簡單的文件上傳的例子。
1、jsp部分的代碼:
1 <%@ page language="java" contentType="text/html; charset=utf-8"
2 pageEncoding="UTF-8"%>
3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
4 <html>
5 <head>
6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7 <title>file upload demo</title>
8 </head>
9 <body>
10 <form action="/servletPro/FileUpload" method="post" enctype="multipart/form-data">
11 <table width="31%" border="0" align="center">
12 <tr bgcolor="#CCCCCC">
13 <th height="26">請選擇要上傳的附件:</th>
14 </tr>
15 <tr>
16 <td><label>上傳文件</label><input type="file" name="file" /></td>
17 </tr>
18 <tr bgcolor="#CCCCCC">
19 <td height="23"><input type="submit" name="submit" value="上傳"></td>
20 </tr>
21 </table>
22 </form>
23 <%
24 if (request.getAttribute("result") != null) {
25 out.println("<script>alert('" + request.getAttribute("result") + "');</script>");
26 }
27 %>
28 </body>
29 </html>
2、在項目WebContent/目錄下創建一個uploadfiles文件夾用來存放上傳的文件。
3、創建一個名為FileUpload的servlet,代碼如下:
package com.hyman.servlet;
public class FileUpload extends HttpServlet {
private static final long serialVersionUID = 1L;
private String uploadFileDir;
private ServletContext sc;
public FileUpload() {
super();
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
uploadFileDir = config.getInitParameter("uploadDir");
sc = config.getServletContext();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String simpleFileName = "";
String fileDir = sc.getRealPath("/") + uploadFileDir;
String message = "文件上傳成功!";
if (ServletFileUpload.isMultipartContent(request)) {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(20 * 1024); //20KB
factory.setRepository(factory.getRepository());
ServletFileUpload upload = new ServletFileUpload(factory);
int maxSize = 2 * 1024 * 1024; //2MB
List formlists = null;
try {
formlists = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
Iterator iterator = formlists.iterator();
while (iterator.hasNext()) {
FileItem formitem = (FileItem) iterator.next();
if (formitem.isFormField()) {
String fieldname = formitem.getFieldName();
//這里是非上傳文件的表單域,可以通過formitem.getString(fieldname)來獲取相應表單字段的值
} else {
//這里是上傳文件的表單域
String name = formitem.getName();
if (formitem.getSize() > maxSize) {
message = "您上傳的文件太大,請重新選擇不超過2M的文件";
break;
}
String fileSize = new Long(formitem.getSize()).toString();
if (name == null || "".equals(name) && "0".equals(fileSize))
continue;
int delimiter = name.lastIndexOf("\\");
simpleFileName = delimiter == -1 ? name : name.substring(delimiter + 1);
File saveFile = new File(fileDir, simpleFileName);
try {
formitem.write(saveFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
request.setAttribute("result", message);
request.getRequestDispatcher("FileUpload.jsp").forward(request, response);
}
}
4、在web.xml中進行初始化參數配置:
1 <servlet>
2 <description></description>
3 <display-name>FileUpload</display-name>
4 <servlet-name>FileUpload</servlet-name>
5 <servlet-class>com.hyman.servlet.FileUpload</servlet-class>
6 <init-param>
7 <param-name>uploadDir</param-name>
8 <param-value>uploadfiles\</param-value>
9 </init-param>
10 </servlet>
11 <servlet-mapping>
12 <servlet-name>FileUpload</servlet-name>
13 <url-pattern>/FileUpload</url-pattern>
14 </servlet-mapping>
五、使用過程中遇到的一些問題及解決辦法
1、每個用戶上傳的文件需要存放在以用戶uid通過MD5加密命名的文件夾中,即需先在uploadfiles目錄下創建一個對應的文件夾再將文件存該文件夾中。
可以如下實現:
1 String fileDir = sc.getRealPath("/") + uploadFileDir;
2 //創建文件夾的路徑
3 String folderName = fileDir + "328ae78f9298e9a00c9dfa673280c17d";
4 File folder = new File(folderName);
5 if (!folder.exists()) {
6 folder.mkdir();
7 }
2、路徑中的"/"改為"\"
可以使用:folderName = folderName.replaceAll("/", "\\\\");
路徑中的"\"改為"/"
可以使用:folderName = folderName.replaceAll("\\\\", "/");
3、在上傳文件的時候還提交了非上傳文件的表單,則讀取文件和其他表單數據的先后順序跟jsp文件中表單的前后順序有關。
4、在項目中,可能同時上傳多份文件,而這些文件的全路徑需以一個特定的連接符連接起來存到數據庫中。我在項目中碰到這么一個問題,只要服務器重新啟動,第一次上傳文件保存的路徑集沒錯,但是之后的上傳都會出現路徑集中附帶之前上傳的文件路徑...而重啟服務器之后的第一次上傳又不會出現這種情況。這個問題着實糾結了很久,后來看到網上有介紹說可能是緩存的問題formitem用完要調用delete()方法清空緩存,這樣處理之后還是沒能解決這個問題。后來突然發現自己將保存每次上傳路徑集的String對象定義成了當前servlet的一個成員變量!!!servlet是單例的,該servlet只要被服務器創建了就會一直保存在服務器中,所以保存每次上傳路徑集的String對象會將每次上傳的文件全都疊加起來。