天天看點

Servlet 實作上傳附件(支援多附件)

一、簡單介紹

     使用 Servlet上傳附件 原理上還是蠻簡單的,首先擷取上傳的附件對象,然後做一些簡單處理 後寫入到指定路徑的磁盤中。

二、準備條件

    common-io.jar  ,下載下傳位址:http://commons.apache.org/io/download_io.cgi

    common-upload.jar ,下載下傳位址:http://jakarta.apache.org/commons/fileupload/

三、實作流程

    首先在建立好的項目中 加入上述的兩個必須jar包,然後根據需求建立好upload.jsp頁面選擇附件,代碼如下:

<%@page import="java.io.File"%>
<%@ 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>Use servlet upload file</title>
</head>
<body>
<form name="uploadForm" method="post" action="servlet/uploadFileServlet" enctype="multipart/form-data">
    附件名稱:<input type="text" name="uploadName" value=""/><br/>
    選擇附件:<input type="file" name="uploadFile"/><br/>
    <input type="submit" value="上傳"/>
</form>
</body>
</html>
           

其中,enctype="multipart/form-data" 是必須加入的,因為原先沒有加入的普通表單是以字元送出到背景的,但是加了之後所有參數全部會以二進制流格式傳入背景,是以背景處理代碼 request.getParameter("paramName") 将傳回空字元串。

基本顯示如下圖:

Servlet 實作上傳附件(支援多附件)

頁面定義好了,接下來建立上面送出的servlet ,代碼如下:

package com.chenghui.servlet;
 
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
 
/**
 * Servlet implementation class UploadFileServlet
 */
public class UploadFileServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    private static final String UPLOAD_PATH = "d:\\attach\\";
    
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadFileServlet() {
        super();
        // TODO Auto-generated constructor stub
    }
 
    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
    }
 
    /**
     * @see Servlet#destroy()
     */
    public void destroy() {
        // TODO Auto-generated method stub
    }
 
    /**
     * 
     * 偶然發現一個現象,當我定義的servlet 中重寫了 service方法,那麼每次調用的是service方法,除非注釋掉該方法才會根據請求挑戰到對象的doPost/doGet中
     * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
     *//*
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("service");
    }*/
 
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        
        //建立一個磁盤檔案的工廠,然後将它 傳遞到servletFileUplaod的執行個體中
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
        
        try {
            //根據request對象擷取所有的檔案集合,這裡包括input标簽輸入的值也屬于FileInput
            List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
            Iterator iterator = fileItemList.iterator();
            String showFileName = "";
            while(iterator.hasNext()){
                FileItem fileItem = (FileItem)iterator.next();
                if(fileItem.isFormField()){ //是否是表單送出域,可以分區是否上傳的附件
                    String name = fileItem.getFieldName();  //input标簽的name
                    String value = fileItem.getString();    //input表單的value
                    if("uploadName".equals(name)){
                        showFileName = value;
                    }
                }else{
                    String fieldName = fileItem.getFieldName();  //表單送出過來的file input标簽中name的屬性值
                    String fileName = fileItem.getName();  //file input上傳的檔案名
                    String contentType = fileItem.getContentType();  //獲得上傳檔案的類型
                    long size = fileItem.getSize();      //上傳檔案的大小
                    
                    String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
                    File saveFile = new File(filePath);
                    
                    fileItem.write(saveFile); //将檔案寫入磁盤中
                    //成功之後 簡單輸出一下
                    PrintWriter out = response.getWriter();
                    out.print("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!");
                    out.close();
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
 
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
 
}
 
 
在web.xml中對應的配置:
           
  <servlet>
      <servlet-name>uploadFileServlet</servlet-name>
      <servlet-class>com.chenghui.servlet.UploadFileServlet</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>uploadFileServlet</servlet-name>
      <url-pattern>/servlet/uploadFileServlet</url-pattern>
  </servlet-mapping>
           

好了基本上可以走一下流程了,在upload.jsp頁面選擇附件後點選“上傳”按鈕,效果顯示如下

Servlet 實作上傳附件(支援多附件)

然後去d:attach 目錄下找上傳的附件,找到了,ok上傳成功!但是顯示的文字有亂碼,這是因為response對象在輸出的時候沒有指定具體的編碼,是以按照預設的iso-8859-1輸出  就亂碼了。

解決方案:在 PrintWriter out = response.getWriter(); 的上面加上

response.setContentType("text/html;charset=UTF-8");

就好了。

Servlet 實作上傳附件(支援多附件)

另外,将上傳的附件是使用org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中的,這也可以用位元組流完成該操作。

/**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        
        //建立一個磁盤檔案的工廠,然後将它 傳遞到servletFileUplaod的執行個體中
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
        
        try {
            //根據request對象擷取所有的檔案集合,這裡包括input标簽輸入的值也屬于FileInput
            List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
            Iterator iterator = fileItemList.iterator();
            String showFileName = "";
            //如果附件位址不存在 就建立一下
            File uploadPath = new File(UPLOAD_PATH);
            if(!uploadPath.exists()){
                uploadPath.mkdir();
            }
            while(iterator.hasNext()){
                FileItem fileItem = (FileItem)iterator.next();
                if(fileItem.isFormField()){ //是否是表單送出域,可以分區是否上傳的附件
                    String name = fileItem.getFieldName();  //input标簽的name
                    String value = fileItem.getString();    //input表單的value
                    if("uploadName".equals(name)){
                        showFileName = value;
                    }
                }else{
                    String fieldName = fileItem.getFieldName();  //表單送出過來的file input标簽中name的屬性值
                    String fileName = fileItem.getName();  //file input上傳的檔案名
                    String contentType = fileItem.getContentType();  //獲得上傳檔案的類型
                    long size = fileItem.getSize();      //上傳檔案的笑答
                    
                    String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
                    //org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中
                    //fileItem.write(new File(filePath));
                    //使用位元組流讀取二進制格式的附件傳給檔案流  然後 寫入磁盤
                    OutputStream outputStream = new FileOutputStream(new File(UPLOAD_PATH,showFileName + fileName.substring(fileName.lastIndexOf("."))));//這裡傳遞父親的檔案夾路徑和目前檔案的名稱
                    InputStream inputStream = fileItem.getInputStream();
                    int length = 0;
                    
                    byte[] buf = new byte[1024];
                    while((length = inputStream.read(buf)) != -1){  //首先根據傳遞的位元組數組将讀取的位元組的數量傳回,在判斷是否讀取的空
                        System.out.println(buf);
                        outputStream.write(buf, 0, length);
                    }
                    inputStream.close();
                    outputStream.close();
                    //成功之後 簡單輸出一下
                    response.setContentType("text/html;charset=UTF-8");
                    PrintWriter out = response.getWriter();
                    out.print("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!");
                    out.close();
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
           

上傳的效果是一樣的。輸出每次讀取的byte數組,效果如下:

Servlet 實作上傳附件(支援多附件)

基本上,使用servlet 上傳附件的功能就已經完成了。

四、多附件上傳

如果有需求需要上傳多個附件,那麼現在這個demo同樣有效果,隻需要稍作修改就好了。

upload.jsp需要增加一個選擇附件的控件,代碼 如下:

<%@page import="java.io.File"%>
<%@ 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>Use servlet upload file</title>
</head>
<body>
<form name="uploadForm" method="post" action="servlet/uploadFileServlet" enctype="multipart/form-data">
    附件名稱:<input type="text" name="uploadName" value=""/><br/>
    選擇附件:<input type="file" name="uploadFile"/><br/>
    
    附件名稱:<input type="text" name="uploadName1" value=""/><br/>
    選擇附件:<input type="file" name="uploadFile2"/><br/>
    
    <input type="submit" value="上傳"/>
</form>
</body>
</html>
           

servlet.java 邏輯其實不用改,因為我們是疊代所有FileItem對象的集合,是以所有的附件和附件資訊全都儲存在這個集合中,簡單提煉了下代碼,具體如下:

 /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
 
        //建立一個磁盤檔案的工廠,然後将它 傳遞到servletFileUplaod的執行個體中
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
        try {
            //根據request對象擷取所有的檔案集合,這裡包括input标簽輸入的值也屬于FileInput
            List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
            Iterator iterator = fileItemList.iterator();
            String showFileName = "";
            String showFileName1 = "";
            //如果附件位址不存在 就建立一下
            File uploadPath = new File(UPLOAD_PATH);
            if(!uploadPath.exists()){
                uploadPath.mkdir();
            }
            while(iterator.hasNext()){
                FileItem fileItem = (FileItem)iterator.next();
                if(fileItem.isFormField()){ //是否是表單送出域,可以分區是否上傳的附件
                    String name = fileItem.getFieldName();  //input标簽的name
                    String value = fileItem.getString();    //input表單的value
                    showFileName = value;  //這裡注意好出場順序,不然就亂套了
                }else{
                    String fieldName = fileItem.getFieldName();  //表單送出過來的file input标簽中name的屬性值
                    String fileName = fileItem.getName();  //file input上傳的檔案名
                    String contentType = fileItem.getContentType();  //獲得上傳檔案的類型
                    long size = fileItem.getSize();      //上傳檔案的笑答
                    
                    String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
                    //org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中
                    //fileItem.write(new File(filePath));
                    //使用位元組流讀取二進制格式的附件傳給檔案流  然後 寫入磁盤
                    OutputStream outputStream = new FileOutputStream(new File(UPLOAD_PATH,showFileName + fileName.substring(fileName.lastIndexOf("."))));//這裡傳遞父親的檔案夾路徑和目前檔案的名稱
                    InputStream inputStream = fileItem.getInputStream();
                    int length = 0;
                    
                    byte[] buf = new byte[1024];
                    while((length = inputStream.read(buf)) != -1){  //首先根據傳遞的位元組數組将讀取的位元組的數量傳回,在判斷是否讀取的空
                        System.out.println(buf);
                        outputStream.write(buf, 0, length);
                    }
                    inputStream.close();
                    outputStream.close();
                    //成功之後 簡單輸出一下
                    out.print("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!");
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            out.close();
        }
    }
           

重新部署項目後運作,選擇好附件,然後送出後檢視源代碼如下:

Servlet 實作上傳附件(支援多附件)

這裡發現沒有換行,是以我在servlet 中使用out.println() 輸出發現頁面依舊沒有換行,隻不過源代碼換行了,具體如下:

Servlet 實作上傳附件(支援多附件)

這樣 源代碼換行了,頁面顯示也換行了。

五、實作下載下傳功能

上傳功能實作完了 之後再來說說下載下傳功能的具體實作。

如果我們把附件放在目前項目下,可以直接通過超連結 進行下載下傳,但是假如附件放在我們上面例子的d:\attach 下那怎麼辦,tomcat 是不可能通路項目以外的資源的。

是以接下來有兩種解決方案:

1、建立一個虛拟目錄

我們釋出項目的時候,也是通過在server.xml配置<Context> 實作将一個虛拟目錄釋出到tomcat伺服器上,不一定這個虛拟目錄上非得有什麼WEB-INF,web.ml 等等之類的檔案,這個目錄專門存放一些附件之類的同樣可以通路。

那麼同理 在server.xml 中加上如下例子:

<Context docBase="D:\attach" path="/img" reloabable="true" />通路的話可以使用 localhost:8080/img/imgName 就好了。

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //擷取需要下載下傳的附件的位址,然後采用輸入流InputStream讀取附件資訊
        String filePath = request.getParameter("filePath");
        if(filePath!=null){
            try{
                File file = new File(filePath);
                //建立輸入流
                InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
                byte[] buffer = new byte[inputStream.available()];
                //傳遞available() 有效的位元組數,可以一次性讀取完畢
                inputStream.read(buffer);
                inputStream.close();
                //清空response,設定response的Header
                response.reset();           
                response.addHeader("Content-Disposition", "attachment;filename=" + new String(file.getName().getBytes("utf-8"),"ISO-8859-1"));
                response.addHeader("Content-Length", "" + file.length());
                
                OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
                response.setContentType("application/octet-stream");
                outputStream.write(buffer);
                outputStream.flush();
                outputStream.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
           

然後把上傳的輸出字元串改一下,加一個超連結,代碼如下:

                    //成功之後 簡單輸出一下

                    out.println("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!<a href='downloadFileServlet?filePath="+filePath+"'>下載下傳</a><br/>");

接下來 重新部署下項目,看看結果。效果如下圖所示:

Servlet 實作上傳附件(支援多附件)

ok,下載下傳附件功能也完成了!

六、上傳附件中遇到問題的解決方案

1、中文亂碼

在上面的例子中 附件顯示名稱和 附件的名稱都是使用的英文名稱,如果改為中文呢? 是以果斷嘗試了一下,結果都亂碼了,當時第一時間想到的是 使用request.setCharacterEncoding("UTF-8"); 但是沒有效果呀,這種設定字元編碼 方式隻适合普通表單的post請求。

後來發現 可以這樣,在建立ServletFileUpload對象之後設定一下頭部編碼,代碼如下:

        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);

        servletFileUpload.setHeaderEncoding("UTF-8");

再試一下,發現 附件的實際名稱沒變亂碼,但是 附件的顯示名稱還是亂碼,這需要單獨設定encoding,原先取參數都是

String value = fileItem.getString();    //input表單的value

需要傳遞encoding ,讓FileItem知道你想得到什麼格式的value,代碼如下:

String value = fileItem.getString("UTF-8");    //input表單的value

再重新部署一下,具體效果如下:

Servlet 實作上傳附件(支援多附件)

位址:http://blog.csdn.net/chenghui0317/article/details/9502143

繼續閱讀