[教學]jsp Web的檔案上傳-FileUpload

這是一個簡單的fileupload程式,把整個upload動作都包裝在UploadTool這個class裡,而在jsp裡再call此class來做檢查及上傳等動作。jar檔需放置WEB-INF/lib/裡,而程式complier後放置WEB-INF/classes/toolkie/裡。

首先需要二個第三方的jar檔 Apache FiluploadApache common io 都下載Binary的jar檔就可以了

檔案的配制結構如下圖:

tomcat文件配制圖

再來利用下面的程式來進行上傳作業

UploadTool.java

package toolkie;

import java.io.File;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
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;
import java.util.*;

public class UploadTool {
    private int buffersize = 4096;
    private int SizeMax = 1024 * 1024;// 1Mbyte最大檔案大小
    private File tempfile = null;
    private String def_upload_dir = null;

    // 用來存parameter
    private Map map = null;
    private Map uploadlist = null;

    // 處始化時把給request把所有的值取出,存入map
    public UploadTool(HttpServletRequest request) throws FileUploadException,
            UnsupportedEncodingException {

        map = new HashMap();
        uploadlist = new HashMap();

        // 建立一個以disk-base的檔案物件
        DiskFileItemFactory factory = new DiskFileItemFactory();

        // 初始化內容
        // 傳送所用的buffer空間
        factory.setSizeThreshold(buffersize);
        // The directory in which temporary files will be located.

        factory.setRepository(tempfile);

        // 建立一個檔案上傳的物件
        ServletFileUpload upload = new ServletFileUpload(factory);

        // 最大檔案大小
        upload.setSizeMax(SizeMax * 10);

        // 每一個Fileitem代表一個form上傳的物件內容ex input type="text"
        List items = null; // 會產生 FileUploadException
        // 把資料從request取出
        items = upload.parseRequest(request); // Parse the request

        Iterator iter = items.iterator();

        while (iter.hasNext()) {// 先把所有參數取得而不先write to file
            FileItem item = (FileItem) iter.next();
            // 一般文字欄位
            if (item.isFormField()) {
                map.put(item.getFieldName(), item.getString("Big5"));
                System.out.println("上傳檔案的其它參數:" + item.getFieldName() + "="
                        + item.getString("Big5"));
            } else {// 上傳檔案欄位
                // or it's a file upload request

                if (item.getSize() > 0) {
                    uploadlist.put(item.getFieldName(), item);
                    System.out.println("上傳檔案:" + item.getFieldName());
                }
            }
        }
    }

    // 設定檔案上傳後存放的地方
    public void setUploadDir(String upload_dir) {
        this.def_upload_dir = upload_dir;
    }

    // 取得所有欄位,包含一般欄位及上傳的欄位
    public Map getAllParameter() {
        Map rvalue = new HashMap();
        rvalue.putAll(map);
        rvalue.putAll(uploadlist);
        return rvalue;
    }

    // 取得某一欄位的值,一般欄位
    public String getParameter(String FieldName) {
        if (map.containsKey(FieldName))
            return String.valueOf(map.get(FieldName));
        else
            return null;
    }

    // 取得某一欄位的值,上傳欄位
    public FileItem getUploadParameter(String FieldName) {
        if (uploadlist.containsKey(FieldName))
            return (FileItem) uploadlist.get(FieldName);
        else
            return null;
    }

    // 檢查上傳資料是否正確
    public String checkUpload() {
        Iterator iter = uploadlist.keySet().iterator();
        while (iter.hasNext()) {
            Object Name = iter.next();
            FileItem item = (FileItem) uploadlist.get(Name);
            String itename = item.getName();
            System.out.println("上傳的檔案為:" + itename);
            if (item.getSize() > SizeMax)
                return "檔案太大!";
        }
        return "";
    }

    // 開始上傳
    public String doUpload(FileItem item, String fileName) {
        String str = "";
        long sizeInBytes = item.getSize();
        // 碓認上傳資料是否有誤
        if (sizeInBytes > SizeMax)
            return "檔案太大!";

        if (sizeInBytes > 0) {

            int index = -1;
            String itename = null;
            if ((index = item.getName().lastIndexOf("\\")) != -1)
                itename = item.getName().substring(index,
                        item.getName().length());
            else
                itename = item.getName();
            // 副檔名
            String formatName = itename.substring(itename.length() - 4,
                    itename.length());

            fileName = (fileName + formatName).toLowerCase();

            System.out.println("上傳檔案檔案名稱:" + fileName);

            File uploadedFile = new File(def_upload_dir + fileName);
            // 會產生 Exception
            try {
                item.write(uploadedFile);

            } catch (Exception e) {
                System.out.println("上傳失敗!" + e.toString());
                str = "上傳失敗!";
            }
            // 會產生 Exception

        }
        return str;
    }

    // 是否存在此上傳欄位資料

    public boolean isExtUpload(String fileName) {
        return uploadlist.containsKey(fileName);
    }
}

這是整個程式最主要的範例 把所有可能會用到的上傳狀況及行為均寫入此Class裡 再來我們寫一個form來當上傳介面及另一個接收檔案的程式

form表單DemoFileUpload.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=BIG5">
<title>Insert title here</title>
</head>
<body>
<b>File Upload</b>
<br>
<form name="UploadForm" enctype="multipart/form-data" method="post"
    action="doupload.jsp">檔案1<input type="file" name="File1"
    size="50" maxlength="20" /> <br>
檔案2<input type="file" name="File2" size="50" maxlength="20" /> <br>
文字<input type="text" name="textfield" size="50" maxlength="20" /> <br>
<input type="submit" value="upload"></form>
</body>
</html>

實際使用doupload.jsp

<%@ page language="java" contentType="text/html; charset=BIG5"
    pageEncoding="BIG5"%>
<%
    //把request傳入UploadTool裡
    toolkie.UploadTool upload = new toolkie.UploadTool(request);
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=BIG5">
<title>Insert title here</title>
</head>
<body>
<%
    //查詢是否錯誤
    String msg = upload.checkUpload();
    if (msg.length() > 0) {
        out.println(msg);
    } else {
        //設定上傳路徑
        upload.setUploadDir(this.getServletContext().getRealPath("."));
        out.println("上傳到此路徑:"
                + this.getServletContext().getRealPath(".") + "<br/>");
        //取得文字檔
        out.println("文字欄位:" + upload.getParameter("textfield")
                + "<br/>");
        //開始上傳
        if (upload.isExtUpload("File1"))
            msg = upload.doUpload(upload.getUploadParameter("File1"),
                    "File1");
        if (msg.length() == 0 && upload.isExtUpload("File2"))
            msg = upload.doUpload(upload.getUploadParameter("File2"),
                    "File2");

        if (msg.length() > 0)
            out.println(msg);
        else
            out.println("上傳成功" + "<br/>");

    }
%>

</body>
</html>

說明:

  • upload需要先設定到上傳的路徑為何
  • 可以直接取得form其它非上傳欄位的值,用法跟request一樣->upload.getParameter(“欄位名”)
  • 上傳指定的欄位檔案及更改其名稱->upload.doUpload(FileItem,”要變成的檔案名稱”)
  • 其中FileItem可以使用upload.getParameter(“欄位名稱”)
  • 如上傳成功則不會有msg回傳,否則則會告知錯誤內容
  • 欄位名稱為傳送的form表單中元素的欄位名稱

以下是執行過程

1.選擇要上傳的檔案

2.按下upload後會告知檔案被上傳的路徑及文字欄位的內容及上傳成功與否

3.可以在上傳路徑裡看到二個剛才上傳的欄案,而名稱已重新命名了

4.可以在Console裡看到整個上傳過程

程式碼的說明均寫在裡面就不在詳述,有興趣的人可以看看,再來會再寫一篇如何使用Eclipse來執行jsp程式,將會使用上面的範例^^

59 thoughts to “[教學]jsp Web的檔案上傳-FileUpload”

  1. 您好,我下載您的範例後執行後,eclipse會回報找不到CLASS
    我就加入了
    再執行一次變成了下面的錯誤
    description The server encountered an internal error () that prevented it from fulfilling this request.

    exception

    javax.servlet.ServletException: java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileItemFactory
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:268)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    請問我的問題是什麼原因產生的呢?
    謝謝

    1. 這應該是您未在classpath裡加入需要的jar檔
      第三方的jar檔
      Apache Filupload及Apache common io
      你可以在文章一開始的地方取得連接,下載後解開裡面的jar
      然後開啟eclipse專案結點按右鍵properties->Java Build Path->Libraies 載入這些jar檔
      或著你可以把jar檔放到你載案目錄下的web-inf/lib目錄下,也有一樣的效果(如果是tomcat Server的話)

  2. 不好意思,打擾了,我參照你的程式碼,去實作檔案上傳的功能,
    但是在本機端測試可以成功將圖片上傳至
    C:\tomcat\webapps\Knowledge2\pic\

    但是在上傳至我自己的主機上運作,在上傳檔案之後仍出現 上傳成功
    但實際進入電腦裡面 pic的資料夾卻沒有檔案存在
    /var/lib/tomcat6/webapps/Knowledge/pic\

    upLoad_DIR=this.getServletContext().getRealPath(“/”)+”pic\\”;
    這是我在上傳檔案的jsp頁面 所設定的路徑,

    因為沒有出現實際的錯誤訊息,所以才冒昧詢問一下,是否有相關的修正方向,可以提供給我參考

    不好意思,謝謝

    1. 原始碼跟linux的環境是那一種可以給我一下嘛?
      我幫你架起來試看看^^
      有可能是你Linux給tomcat的權限不夠 有幾個地方要設
      一個是tomcat啟動的user
      一個是java的policy
      還有tomcat好像也要地方設
      建議你用下載的tomcat解壓縮來設定 可以參考我的文章
      用agt-get好像有這種問題…
      回答不知道是不是你想要的

  3. 用你的方是無法直行出現
    jar檔有放到lib內就ok 但而程式complier後放置WEB-INF/classes/toolkie/裡。
    這就看不懂囉

    1. 我幫你補上一個這些檔案的放置位置圖喔…
      您可以看看!有問題再請告知

  4. 可以請問一下您嗎!

    我使用java寫了一個搜尋功能
    有使用到main
    是我的只要動作與印出

    也有使用到HttpServletRequest request
    是要從jsp那邊拿到text物件的資訊
    拿到的參數傳入main那邊執行結束後要印回jsp
    不知道這辦法對不對

    1. 我不太懂你的意思
      main是指public static void main(String args[])嘛?
      那是application的寫法喔
      你要使用你寫的程式內容,可以利用servlet或是jsp去呼叫你的java class
      (跟你在main裡呼叫方式一樣)

      1. 嗯 是public static void main(String args[])
        但是我使用doget去丟值 他會在丟進去main去執行嗎?

        請問一下你有通訊方式嗎 ??facebook 或是 yahoo

          1. 不是網路怪怪的
            你看不到回覆的訊息是因為我blog的快取因素

        1. 你可以給我範例嘛?
          你直接mail給我就可以了@我一直都有在收信喔

  5. @Myron, 是public static void main(String args[])
    我使用doget把jsp 文字區塊內的值傳入執行
    再使用最外層
    public class Searchss extends HttpServlet{
    public static String phrase;
    宣告一個變數phrase 要丟入main內去執行
    但是好像無法丟入

    請問一下您有通訊方式嗎? 例如:facebook,yahoo

  6. @Myron, 是public static void main(String args[])
    我使用doget把jsp 文字區塊內的值傳入執行
    再使用最外層
    public class Searchss extends HttpServlet{
    public static String phrase;
    宣告一個變數phrase 要丟入main內去執行
    但是好像無法丟入

    請問一下您有通訊方式嗎? 例如:facebook,yahoo

  7. 請問如何把程式改成上傳什麼檔案在目錄上就是那原本檔案的檔名呢?

    1. File uploadedFile = new File(def_upload_dir +item.getName());
      把上傳要存放的檔名修改成item.getName()的名稱就可以了

  8. doupload.jsp 執行時有誤,
    toolkie.UploadTool cannot be resolved to a type

    toolkie.UploadTool upload = new toolkie.UploadTool(request);

    Stacktrace:
    org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:92)
    org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:330)
    org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:423)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:317)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:295)
    org.apache.jasper.compiler.Compiler.compile(Compiler.java:282)
    org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:586)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:317)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

  9. 您好,看完這篇文章有一個小問題想要請教您。
    若是只是想要取得欄位裡「檔案的完整路徑」要怎麼寫呢?

    如果用 request.getParameter(“欄位名稱”) 他只會列出檔名,不會是它的完整路徑

    不好意思,才剛碰這方面沒多久,想要用其他方法寫上傳,可是一直沒辦法取得完整檔案路徑>”<
    搜尋了很久好像都沒找到相關資料…不知道這個問題是不是有解的呢?謝謝您~

    1. 阿…真是不好意思,留完言後亂試居然試出來了QQ
      File file = new File(request.getParameter(“欄位名稱”));
      這樣就可以讀到檔案了!
      原本以為一定要完整路徑才可以,不好意思打擾您了>”<

  10. Dear sir,
      我的環境是Eclipse+JRE1.6+Tomact6.0,
      我在單獨執行UploadTool.java(為了將之編譯成.class),
      出現description  The requested resource () is not available.
      能否煩請協助告知原因,
      麻煩了~~~

  11. 你好:

    最近剛好也在寫這一塊,剛好看到這篇文章,實在是受益良多!

    但是我有一個小小的問題就是我在upload.getParameter 的時候

    如果傳進來的是中文會變成亂碼,這邊要怎麼解決啊??

    如果我們用一般的request的話可以用.getBytes(“ISO8859-1”),”UTF-8″來解決,

    但是因為現在是用upload.getParameter 的方式,就沒有上述的方法可用了! 這有解嗎??

    還有一個問題就是我希望做到上傳之後的檔案名稱跟原來的檔名一樣,我試過你在上面說的方法,可是似乎無效,是我用錯了嗎?還是哪邊沒注意到???

    還有就是說如果上傳放置的地方,我們現在是用絕對路徑來做為存放位置,可改用相對位置嗎??

    小弟剛學沒多久,有一些問題希望大大可以幫我解惑 感激不進

    1. @skyghot,
      1.你可以把你的網頁 content-type設成utf8 接收端也一樣
      還有,把文件的編碼也設成utf8 <---這很重要 基本上就可以解決了 2.item.getName() 時 它就是原本的名字了,你可以把它存起來,要存回時再把它當檔名 3.相對路徑也可以,只是當你在開發時,跟你上線到主機時 你會發生你的相對位置可能不一樣 這樣子會讓你十分不好管理 this.getServletContext().getRealPath(".") 這個會抓到你網頁的http://xxx.xxx.xxx/ 的實際路徑 這樣子存在這裡才可以在網頁上看的到

      1. @yku,

        1.你說的那些我基本上都有設定了但是不知道是否有設對,因為他沒有起作用

        jsp中

        我做了這些設定 文件編碼也是使用utf-8

        其實upload.getParameter亂碼的問題我已經解決了,就是在java檔中
        if (item.isFormField()) {
        map.put(item.getFieldName(), item.getString(“Big5”)); <———————–改設成utf-8
        System.out.println("上傳檔案的其它參數:" + item.getFieldName() + "="
        + item.getString("Big5"));<————————-改設成utf-8

        但是這樣如果是上傳的檔案部分就沒辦法這樣改了,所以如果我檔案是中文名稱的話,上傳之後就會變成亂碼,不知道這邊有沒有解??

        2.你說item.getName()這個是在哪裡使用?? 在java檔裡面嗎 還是在jsp檔裡面
        我是在我的jsp黨裡面寫了
        String itename =upload.getUploadParameter("imgFile").getName();
        結果得到的是完整的檔案路徑,而不是只有檔名而已,因為我想說你 upload.doUpload("","filename")這個
        function裡面一定要傳檔名進去,所以我試圖在jsp檔案中,要call這個upload.doUpload之前把檔名抓到然後再送進去~~~這邊是我哪邊用錯了嗎,請指教囉

        3.再來就是說我昨天試了用相對路徑的方式,好像不行喔,會想要用相對路徑是有原因的,因為我是因為有兩個project, project1 project2 , 我要上傳圖片的程式是寫在project2中,然後存檔路徑是要在project1中,如果用你說的那個方式,可能就不適合了= =

        再來我發現一個問題就是說,因為我程式裡面其實很多東西要上傳,有一般的的text,textarea,還有file 因為有file的關係所以使用了enctype="multipart/form-data"
        這樣乍看之下好像滿OK的,但是如果今天file欄位如果沒上傳東西,好像連帶著連text跟textarea都會收不到參數耶~~~這有解嗎??

        問題好像有點多 , 抱歉 ,因為是新手還滿多不懂的~~感謝你的回答喔

          1. @yku,
            恩~~~謝謝你囉!XD

            上面提的問題有些是我程式的問題,不過大部分是都已經解決了

            你的程式我之後有空再看囉,不管如何 總之謝謝你囉

            對了~~~看是不是方便留個MSN或是FB,之後我們可以討論問題或交個朋友囉

            方便的話就寄mail囉

            再次感謝你XD

  12. hihi
    能請叫我要把圖片(上傳,下載)並讓本機端可以接收到也是要利用像你現在這個範例嗎?

    1. 圖片上傳可以
      下載的話你只要提供圖片的連結就可以讓它人另存檔案了

  13. 您好很開心能夠看到您無私的分享
    但我在試用這個範例的時候發生一點問題

    就是

    這段程式碼放進去會顯示錯誤
    toolkie的下面是要放置UploadTool.java 還是 UploadTool.class呢??

    Multiple annotations found at this line:
    – toolkie.UploadTool cannot be resolved
    to a type
    – Line breakpoint:newfood1.jsp [line: 5]
    我已經把檔案都放進去了

    1. @penguin,
      src裡的話tookie是放UploadTool.java它會自己complier 並copy到
      Web-INFO\classes的tookie\UploadTool.class
      預設的WTP工具是這樣子做的

  14. 我使用您的範例執行 他可以執行但是只顯示這樣

    上傳到此路徑:D:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\guji\.
    文字欄位:uuu
    上傳成功

    好像只有傳至戰存空間
    我CLASS黨都沒有改

    請問要怎麼設定讓我控制我所儲存的名稱
    還有儲存的資料夾

    由於我是要做線上資料庫
    我要讓帳號變成資料夾名稱,他的照片名稱我要使用一個代號(例如帳號後面加上_1)
    例如資料表=
    picture/penguin(帳號)/penguin_1
    picture/penguin(帳號)/penguin_2
    picture/may(帳號)/may_1

    中間儲存的位置可以改變
    儲存的黨名也要可以控制

    可否請大大幫我解決一下
    小弟目前正努力突破當中

    1. // 設定檔案上傳後存放的地方
      public void setUploadDir(String upload_dir)
      用這個function去設定您要存放檔案的路徑
      ex
      userid/
      或著需要我做範例給您看?

  15. @yku,
    急急~
    按照以上的方法做了一次,再從你FileUpload/..settings/下的files
    copy 至web-inf 才能成功運行,
    版面也出現成功的樣子,
    我再到該floder下看,卻没有該picture,
    你能否估計到是哪里出錯/什么原因,
    希望尽快答覆~
    感謝~

      1. @yku,
        我在UPLOAD之後DISPLAY UPLOAD 的路徑,
        我根據路徑去看,就是没有..@@..
        你有用JSPSmartUpload這方法 嗎?

  16. 喵嗚~
    所以一定要有html去轉嗎?
    因為我直接執行jsp(我們公司的那個檔案)都會叫我下載耶@@
    我的電腦要罷工(哭哭)

  17. 嗚嗚嗚~
    我學你的這篇還是失敗
    人在昏了(睡太少)
    話說要如何讓他執行IE就能Run我的dll啊>”<
    淚奔~~

  18. 你好:
    我照程式做了一次
    成功一半
    因為 file1每次一定能上傳
    但是假如搭配file2
    就會出現上傳失敗
    但是去看資料夾
    file1都有上傳

    而file2永遠上傳是錯的~
    不知道是否哪兒沒有注意到?

      1. @yku
        檔案沒上傳 會顯示程式裡說的錯誤訊息–>上傳失敗
        現在已經解決了~^^

        但是目前遇到一個狀況
        upload.getUploadParameter(“File1”)
        要怎麼取出前面name=XXX.jPG的字串??

        1. @yku,大大不好意思哦`

          我的眼睛都濛了 前面回應就有說了
          沒看清楚~^^””

  19. 我想要取出檔名來存
    但是一直取不到完整的檔名
    請問大大
    不知道在.jsp那頁要怎麼取出來??

    1. @菜菜鳥,
      你看FileUploadTool裡有用到
      item.getFieldName() 這就是檔案名稱了

      1. @yku, 謝謝yku大大的回應~^^
        我才剛學~觀念還不是很清楚~
        非常謝謝大大的指導

  20. 您好:
    雖然不確定是不是只有我會這樣,但是在doupload.jsp的程式中的File2這段乎會造成程式方面的問題,下面是程式碼:
    if (msg.length() == 0 && upload.isExtUpload(“File2”))
    msg = upload.doUpload(upload.getUploadParameter(“File1”), “File2”);

    getUploadParameter(“File1”)是否應該是getUploadParameter(“File2”)??因為我傳兩個圖檔的時候他會跟我說找不到file而失敗,謝謝

    1. @@(,
      if (msg.length() == 0 && upload.isExtUpload(“File2”))
      msg = upload.doUpload(upload.getUploadParameter(“File1”),
      “File2”);

      這裡應該改成
      if (msg.length() == 0 && upload.isExtUpload(“File2”))
      msg = upload.doUpload(upload.getUploadParameter(“File2”),
      “File2”);

  21. 謝謝你的教學,但有個問題
    我見到你可以同時上傳檔案和拿到文字欄位
    我現在有1個FORM 在upload.jsp(上傳圖片)
    但form之中除了 FILE 的INPUT 之外,我還有其他 INPUT
    例如 checkbox,以平常servlet的 拿的方法是用 getPrameterValue()
    會拿到array

    但以你這樣做法,怎樣可以同時上傳到圖片,而且拿到文字欄位,更能夠取得checkbox 的Array value 呢?
    因為我正在做add 的功能,因為要用到file upload 所以 拿的方法不同了……卡了很久

    1. @VICKY200456,
      你可以從
      map.put(item.getFieldName(), item.getString(“Big5”));
      System.out.println(“上傳檔案的其它參數:” + item.getFieldName() + “=”
      + item.getString(“Big5″));
      這裡修改,當FieldName已存在map裡時
      if(map.containKey(item.getFieldName())==true)
      直接取用map值
      String v = map.get(item.getFieldName());
      map.put(key,v+”,”+item.getString(“Big5”));

  22. 請教一下若要將檔案存放於另外一台的DATASERVER
    其路徑該如何設定呢?

    1. 可以考慮用區域網路連線成”網路磁碟機”然後存檔案,不過十分不建議這樣子做,因為如果內網有狀況可能會存不進去等問題,建議把server放在dataserver,然後再把db放在另一台。

發表迴響