天天看點

Java Web 檔案上傳下載下傳1檔案上傳概述2 commons-fileupload3 檔案上傳之細節4 檔案下載下傳5 實作一個圖檔外鍊網站6 實作一個資源托管網站

文章目錄

  • 1檔案上傳概述
    • 1.1檔案上傳的作用
    • 1.2檔案上傳對頁面的要求
    • 1.3 比對檔案上傳表單和普通文本表單的差別
    • 1.4 對普通文本表單的測試
    • 1.5 檔案上傳對Servlet的要求
  • 2 commons-fileupload
    • 2.1 fileupload概述
    • 2.2 fileupload簡單應用
    • 2.3 簡單上傳示例
      • 2.3.1 index.jsp
      • 2.3.2 FileUploadServlet
  • 3 檔案上傳之細節
    • 3.1 把上傳的檔案放到WEB-INF目錄下
    • 3.2 檔案名稱(完整路徑、檔案名稱)
    • 3.3 中文亂碼問題
      • 3.3.1 檔案名稱中包含中文時
      • 3.3.2 檔案内容中包含中文時
    • 3.3 上傳檔案同名問題(檔案重命名)
      • 3.5 一個目錄不能存放過多的檔案(存放目錄打散)
      • 3.7 上傳的單個檔案的大小限制
      • 3.8 緩存大小與臨時目錄
  • 4 檔案下載下傳
  • 5 實作一個圖檔外鍊網站
    • 5.1 項目結構
    • 5.2 具體步驟
      • 5.2.1 在項目中導入`commons-fileupload-1.3.2.jar`和`commons-io-2.5.jar`
      • 5.2.1 在src下建立`com.zxy97.img.servlet`包
      • 5.2.2 在該包下建立UploadServlet.java
      • 5.2.3 建立`webroot/WEB-INF/web.xml`,用來配置servlet
      • 5.2.4 建立`webroot/index.jsp`
  • 6 實作一個資源托管網站
    • 6.1 建立`webroot/WEB-INF/config.properties`
    • 6.2 建立`com/zxy97/img/GetPath.java`
    • 6.3 建立`com.zxy97.img/UploadServlet.java`
    • 6.4 建立`webroot/WEB-INF/web.xml`
    • 6.5 建立webroot/index.jsp
今天傍晚看了李宏利老師的PPT,感觸很深,自己很早就實作了檔案上傳下載下傳,可是一直沒有機會梳理,看了他的PPT後,花了三小時寫下此文。本文1-3參照老師的PPT,4-6貼出自己的項目。

1檔案上傳概述

1.1檔案上傳的作用

例如網盤就是用來上傳下載下傳檔案的,填寫一個完整的履歷還需要上傳照片。

1.2檔案上傳對頁面的要求

上傳檔案的要求比較多,需要記一下:

  1. 必須使用表單,而不能是超連結;
  2. 表單的method必須是POST,而不能是GET;
  3. 表單的enctype必須是multipart/form-data;
  4. 在表單中添加file表單字段,即

    <input type=”file”/>

例如:

<form action="${pageContext.request.contextPath}/FileUploadServlet" method="post" enctype="multipart/form-data">
	使用者名:<input type="text" name="username"/><br/>
	檔案1:<input type="file" name="file1"/><br/>
	檔案2:<input type="file" name="file2"/><br/>
	<input type="submit" value="送出"/>
</form>
    
                

1.3 比對檔案上傳表單和普通文本表單的差別

通過

httpWatch

檢視

檔案上傳表單

普通文本表單

的差別。

  • 檔案上傳表單的

    enctype="multipart/form-data"

    ,表示多部件表單資料;
  • 普通文本表單可以不設定enctype屬性:
    • method="post"

      時,enctype的預設值為

      application/x-www-form-urlencoded

      ,表示使用url編碼正文;
    • method="get"

      時,enctype的預設值為

      null

      ,沒有正文,是以就不需要enctype了。

1.4 對普通文本表單的測試

通過httpWatch測試,檢視表單的請求資料正文,我們發現請求中隻有檔案名稱,而沒有檔案内容。也就是說,當表單的

enctype不是multipart/form-data

時,請求中不包含檔案内容,而隻有檔案的名稱,這說明普通文本表單中

input:file

input:text

沒什麼差別了。

Java Web 檔案上傳下載下傳1檔案上傳概述2 commons-fileupload3 檔案上傳之細節4 檔案下載下傳5 實作一個圖檔外鍊網站6 實作一個資源托管網站

測試-前端頁面

Java Web 檔案上傳下載下傳1檔案上傳概述2 commons-fileupload3 檔案上傳之細節4 檔案下載下傳5 實作一個圖檔外鍊網站6 實作一個資源托管網站

測試-請求正文

Java Web 檔案上傳下載下傳1檔案上傳概述2 commons-fileupload3 檔案上傳之細節4 檔案下載下傳5 實作一個圖檔外鍊網站6 實作一個資源托管網站

測試-請求頭

1.5 檔案上傳對Servlet的要求

當送出的表單是檔案上傳表單時,那麼對Servlet也是有要求的。

首先要肯定一點,檔案上傳表單的資料也是被封裝到request對象中的。

request.getParameter(String)

方法擷取指定的表單字段字元内容,但檔案上傳表單已經不在是字元内容,而是位元組内容,是以失效。

這時可以使用request的getInputStream()方法擷取ServletInputStream對象,它是InputStream的子類,這個ServletInputStream對象對應整個表單的正文部分(從第一個分隔線開始,到最後),這說明我們需要的解析流中的資料。當然解析它是很麻煩的一件事情,而Apache已經幫我們提供了解析它的工具:commons-fileupload。

可以嘗試把request.getInputStream()這個流中的内容列印出來,再對比httpWatch中的請求資料。

public void doPost(HttpServletRequest request, HttpServletResponse response) 
	throws ServletException, IOException {
	InputStream in = request.getInputStream();
	String s = IOUtils.toString(in); 
	System.out.println(s);
}
/*
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="username"
hello
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
aaa
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="file2"; filename="b.txt"
Content-Type: text/plain
bbb
-----------------------------7ddd3370ab2--
*/

                

2 commons-fileupload

為什麼使用fileupload?

上傳檔案的要求比較多,需要記一下:

  • 必須是

    POST

    表單;
  • 表單的enctype必須是

    multipart/form-data

  • 在表單中添加file表單字段,即

    <input type="file"/>

Servlet的要求:

  • 不能再使用request.getParameter()來擷取表單資料;
  • 可以使用request.getInputStream()得到所有的表單資料,而不是一個表單項的資料;

這說明不使用fileupload,我們需要自己來對request.getInputStream()的内容進行解析。

2.1 fileupload概述

fileupload是由apache的commons元件提供的上傳元件。它最主要的工作就是幫我們解析request.getInputStream()。

fileupload元件需要的JAR包有:

  1. commons-fileupload.jar

    ,核心包;
  2. commons-io.jar

    ,依賴包。

下載下傳位址:http://cdn.zxy97.com/s/20180930194932.zip

2.2 fileupload簡單應用

fileupload的核心類有:DiskFileItemFactory、ServletFileUpload、FileItem。

使用fileupload元件的步驟如下:

  1. 建立工廠類DiskFileItemFactory對象:
  2. 使用工廠建立解析器對象:
  3. 使用解析器來解析request對象:
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
List<FileItem> list = fileUpload.parseRequest(request);

                

介紹

FileItem

類,它才是我們最終要的結果。

一個FileItem對象對應一個表單項(表單字段)。一個表單中存在檔案字段和普通字段,可以使用FileItem類的isFormField()方法來判斷表單字段是否為普通字段,如果不是普通字段,那麼就是檔案字段了。

  • String getName():擷取檔案字段的檔案名稱;
  • String getString():擷取字段的内容,如果是檔案字段,那麼擷取的是檔案内容,當然上傳的檔案必須是文本檔案;
  • String getFieldName():擷取字段名稱,例如:,傳回的是username;
  • String getContentType():擷取上傳的檔案的類型,例如:text/plain。
  • int getSize():擷取上傳檔案的大小;
  • boolean isFormField():判斷目前表單字段是否為普通文本字段,如果傳回false,說明是檔案字段;
  • InputStream getInputStream():擷取上傳檔案對應的輸入流;
  • void write(File):把上傳的檔案儲存到指定檔案中。

2.3 簡單上傳示例

現在寫一個簡單的上傳示例:

  • 表單包含一個使用者名字段,以及一個檔案字段;
  • Servlet儲存上傳的檔案到uploads目錄,顯示使用者名,檔案名,檔案大小,檔案類型。

2.3.1 index.jsp

第一步:完成

index.jsp

,隻需要一個表單。注意表單必須是post的,而且enctype必須是mulitpart/form-data的。

<form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
	使用者名:<input type="text" name="username"/><br/>
	檔案1:<input type="file" name="file1"/><br/>
	<input type="submit" value="送出"/>
</form>
                

2.3.2 FileUploadServlet

第二步:完成

FileUploadServlet

public void doPost(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
	// 因為要使用response列印,是以設定其編碼
	response.setContentType("text/html;charset=utf-8");
	
	// 建立工廠
	DiskFileItemFactory dfif = new DiskFileItemFactory();
	// 使用工廠建立解析器對象
	ServletFileUpload fileUpload = new ServletFileUpload(dfif);
	try {
		// 使用解析器對象解析request,得到FileItem清單
		List<FileItem> list = fileUpload.parseRequest(request);
		// 周遊所有表單項
		for(FileItem fileItem : list) {
		// 如果目前表單項為普通表單項
		if(fileItem.isFormField()) {
			// 擷取目前表單項的字段名稱
			String fieldName = fileItem.getFieldName();
			// 如果目前表單項的字段名為username
			if(fieldName.equals("username")) {
				// 列印目前表單項的内容,即使用者在username表單項中輸入的内容
				response.getWriter().print("使用者名:" + fileItem.getString() + "<br/>");
			}
		} else {//如果目前表單項不是普通表單項,說明就是檔案字段
			String name = fileItem.getName();//擷取上傳檔案的名稱
			// 如果上傳的檔案名稱為空,即沒有指定上傳檔案
			if(name == null || name.isEmpty()) {
				continue;
			}
			// 擷取真實路徑,對應${項目目錄}/uploads,當然,這個目錄必須存在
			String savepath = this.getServletContext().getRealPath("/uploads");
			// 通過uploads目錄和檔案名稱來建立File對象
			File file = new File(savepath, name);
			// 把上傳檔案儲存到指定位置
			fileItem.write(file);
			// 列印上傳檔案的名稱
			response.getWriter().print("上傳檔案名:" + name + "<br/>");
			// 列印上傳檔案的大小
			response.getWriter().print("上傳檔案大小:" + fileItem.getSize() + "<br/>");
			// 列印上傳檔案的類型
			response.getWriter().print("上傳檔案類型:" + fileItem.getContentType() + "<br/>");
			}
		}
	} catch (Exception e) {
		throw new ServletException(e);
	} 
}

                

3 檔案上傳之細節

3.1 把上傳的檔案放到WEB-INF目錄下

如果沒有把使用者上傳的檔案存放到WEB-INF目錄下,那麼使用者就可以可能通過浏覽器直接通路上傳的檔案,這是非常危險的。

假如說使用者上傳了一個a.jsp檔案,然後使用者在通過浏覽器去通路這個a.jsp檔案,那麼就會執行a.jsp中的内容,如果在a.jsp中有如下語句:

Runtime.getRuntime().exec("shutdown –s –t 1");

那麼你就會…

通常我們會在WEB-INF目錄下建立一個uploads目錄來存放上傳的檔案,而在Servlet中找到這個目錄需要使用ServletContext的

getRealPath(String)

方法。

例如在我的upload1項目中有如下語句:

ServletContext servletContext = this.getServletContext();
   String savepath = servletContext.getRealPath("/WEB-INF/uploads");
   /*
   savepath為:F:\tomcat6_1\webapps\upload1\WEB-INF\uploads
   */
                

3.2 檔案名稱(完整路徑、檔案名稱)

IE6擷取的上傳檔案名稱是完整路徑,而其他浏覽器擷取的上傳檔案名稱隻是檔案名稱而已。浏覽器差異的問題我們還是需要處理一下的。

String name = file1FileItem.getName();
	response.getWriter().print(name);
                

使用不同浏覽器測試,其中IE6就會傳回上傳檔案的完整路徑,不知道IE6在搞什麼,這給我們帶來了很大的麻煩,就是需要處理這一問題。

處理這一問題也很簡單,無論是否為完整路徑,我們都去截取最後一個“\”後面的内容就可以了。

String name = file1FileItem.getName();
	int lastIndex = name.lastIndexOf("\\");//擷取最後一個“\”的位置
	if(lastIndex != -1) {//注意,如果不是完整路徑,那麼就不會有“\”的存在。
	   name = name.substring(lastIndex + 1);//擷取檔案名稱
	}
	response.getWriter().print(name);
                

3.3 中文亂碼問題

3.3.1 檔案名稱中包含中文時

需要設定編碼,commons-fileupload元件為我們提供了兩種設定編碼的方式:

  • request.setCharacterEncoding(String):這種方式是我們最為熟悉的方式了;
  • fileUpload.setHeaderEncdoing(String):這種方式的優先級高于前一種。

3.3.2 檔案内容中包含中文時

通常我們不需關心上傳檔案的内容,因為我們會把上傳檔案儲存到硬碟上。

也就是說,檔案原來是什麼樣子,到伺服器這邊還是什麼樣子,但是如果你有這樣的需求,非要在控制台顯示上傳的檔案内容,那麼你可以使用

fileItem.getString("utf-8")

來處理編碼。

即:文本檔案内容和普通表單項内容使用

FileItem類的getString("utf-8")

來處理編碼。

3.3 上傳檔案同名問題(檔案重命名)

通常我們會把使用者上傳的檔案儲存到uploads目錄下,但如果使用者上傳了同名檔案呢?這會出現覆寫的現象。處理這一問題的手段是使用UUID生成唯一名稱,然後再使用“_”連接配接檔案上傳的原始名稱。

例如使用者上傳的檔案是

我的一寸照片.jpg

,在通過處理後,檔案名稱為:

891b3881395f4175b969256a3f7b6e10_我的一寸照片.jpg

,這種手段不會使檔案丢失擴充名,并且因為

UUID

的唯一性,上傳的檔案同名,但在伺服器端是不會出現同名問題的。

public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	request.setCharacterEncoding("utf-8");
	DiskFileItemFactory dfif = new DiskFileItemFactory();
	ServletFileUpload fileUpload = new ServletFileUpload(dfif);
	try {
	     List<FileItem> list = fileUpload.parseRequest(request);
	     //擷取第二個表單項,因為第一個表單項是username,第二個才是file表單項
	     FileItem fileItem = list.get(1);
	     String name = fileItem.getName();//擷取檔案名稱
		
	    // 如果用戶端使用的是IE6,那麼需要從完整路徑中擷取檔案名稱
	    int lastIndex = name.lastIndexOf("\\");
	    if(lastIndex != -1) {
		name = name.substring(lastIndex + 1);
	    }
			
		// 擷取上傳檔案的儲存目錄
	     String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");
	     String uuid = CommonUtils.uuid();//生成uuid
	     String filename = uuid + "_" + name;//新的檔案名稱為uuid + 下劃線 + 原始名稱
			
	     //建立file對象,下面會把上傳檔案儲存到這個file指定的路徑
	     //savepath,即上傳檔案的儲存目錄
	     //filename,檔案名稱
	     File file = new File(savepath, filename);
			
	     // 儲存檔案
	      fileItem.write(file);
	} catch (Exception e) {
		throw new ServletException(e);
	} 
  }
                

3.5 一個目錄不能存放過多的檔案(存放目錄打散)

一個目錄下不應該存放過多的檔案,一般一個目錄存放1000個檔案就是上限了,如果在多,那麼打開目錄時就會很“卡”。你可以嘗試列印C:\WINDOWS\system32目錄,你會感覺到的。

也就是說,我們需要把上傳的檔案放到不同的目錄中。但是也不能為每個上傳的檔案一個目錄,這種方式會導緻目錄過多。是以我們應該采用某種算法來“打散”!

打散的方法有很多,例如使用日期來打散,每天生成一個目錄。也可以使用檔案名的首字母來生成目錄,相同首字母的檔案放到同一目錄下。

  • 日期打散算法:如果某一天上傳的檔案過多,那麼也會出現一個目錄檔案過多的情況;
  • 首字母打散算法:如果檔案名是中文的,因為中文過多,是以會導緻目錄過多的現象。

我們這裡使用hash算法來打散:

擷取檔案名稱的

hashCode

擷取hCode的低4位,然後轉換成16進制字元;

擷取hCode的5~8位,然後轉換成16進制字元;

使用這兩個16進制的字元生成目錄鍊。例如低4位字元為

5

//擷取檔案名的hashCode
	int hCode = name.hashCode();
	//擷取hCode的低4位,并轉換成16進制字元串
	String dir1 = Integer.toHexString(hCode & 0xF);
	//擷取hCode的低5~8位,并轉換成16進制字元串
	String dir2 = Integer.toHexString(hCode >>> 4 & 0xF);
	//與檔案儲存目錄連接配接成完整路徑
	savepath = savepath + "/" + dir1 + "/" + dir2;
	//因為這個路徑可能不存在,是以建立成File對象,再建立目錄鍊,確定目錄在儲存檔案之前已經存在
	new File(savepath).mkdirs();
                

3.7 上傳的單個檔案的大小限制

有時我們需要限制一個請求的大小。也就是說這個請求的最大位元組數(所有表單項之和)!實作這一功能也很簡單,隻需要調用ServletFileUpload類的setSizeMax(long)方法即可。

例如

fileUpload.setSizeMax(1024 * 10);

,則設定整個請求的上限為10KB。當請求大小超出10KB時,

ServletFileUpload類的parseRequest()

方法會抛出

FileUploadBase.SizeLimitExceededException

異常。

3.8 緩存大小與臨時目錄

大家想一想,如果我上傳一個藍光電影,先把電影儲存到記憶體中,然後再通過記憶體copy到伺服器硬碟上,那麼你的記憶體能吃的消麼?

是以fileupload元件不可能把檔案都儲存在記憶體中,fileupload會判斷檔案大小是否超出10KB,如果是那麼就把檔案儲存到硬碟上,如果沒有超出,那麼就儲存在記憶體中。

10KB是fileupload預設的值,我們可以來設定它。

當檔案儲存到硬碟時,fileupload是把檔案儲存到系統臨時目錄,當然你也可以去設定臨時目錄。

public void doPost(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
	request.setCharacterEncoding("utf-8");
	DiskFileItemFactory dfif = new DiskFileItemFactory(1024*20, new File("F:\\temp"));
	ServletFileUpload fileUpload = new ServletFileUpload(dfif);
	
	try {
		List<FileItem> list = fileUpload.parseRequest(request);
		FileItem fileItem = list.get(1);
		String name = fileItem.getName();
		String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");
	
		// 儲存檔案
		fileItem.write(path(savepath, name));
	} catch (Exception e) {
		throw new ServletException(e);
	} 
}

                

4 檔案下載下傳

檔案下載下傳的核心代碼就三行:

response.addHeader("Content-Disposition", "attachment;filename=" + childFileName);
	response.addHeader("Content-Length", childFileLength);
	response.setContentType("application/octet-stream");
                

下面給出一個檔案下載下傳的方法實作:

/**
     * @param filePath
     * @param request
     * @param response
     * @return 
     * @throws java.io.FileNotFoundException
     * @throws java.io.UnsupportedEncodingException
     * @描述 檔案下載下傳
     */
    public static boolean downloadFile(String filePath, HttpServletRequest request, HttpServletResponse response)
    		throws FileNotFoundException, UnsupportedEncodingException, IOException {
        boolean isDownload = false;
        File childFile = new File(filePath);
        if (!childFile.exists()) {
            System.out.println("檔案不存在或者已删除-下載下傳失敗!" + childFile.getAbsolutePath());
        } else {
            try (InputStream fis = new FileInputStream(childFile); OutputStream os = response.getOutputStream()) {
                String childFileName = URLEncoder.encode(childFile.getName(), "utf-8");
                String childFileLength = childFile.length() + "";
                response.addHeader("Content-Disposition", "attachment;filename=" + childFileName);
                response.addHeader("Content-Length", childFileLength);
                response.setContentType("application/octet-stream");
                int data;
                while ((data = fis.read()) != -1) {
                    os.write(data);
                }
                isDownload =true;
            }
        }
        return isDownload;
    }
    
    
                

5 實作一個圖檔外鍊網站

單就檔案上傳功能,現在你就可以來實作一個資源托管網站(http://cdn.zxy97.com)或者一個圖檔外鍊網站(http://img.zxy97.com),以下我将介紹實作圖檔外鍊網站的步驟。

5.1 項目結構

Java Web 檔案上傳下載下傳1檔案上傳概述2 commons-fileupload3 檔案上傳之細節4 檔案下載下傳5 實作一個圖檔外鍊網站6 實作一個資源托管網站

5.2 具體步驟

5.2.1 在項目中導入

commons-fileupload-1.3.2.jar

commons-io-2.5.jar

commons-fileupload-1.3.2.jar

commons-io-2.5.jar

的下載下傳位址:http://cdn.zxy97.com/s/20180930194932.zip

下載下傳好後解壓得到兩個jar檔案,在webroot/WEB-INF下建立

lib

檔案夾,複制進去。

然後再用IDE工具導入到項目中。

5.2.1 在src下建立

com.zxy97.img.servlet

com.zxy97

是我的域名(zxy97.com)的反寫,如果你想把項目直接跑起來,按照該步驟進行即可。

5.2.2 在該包下建立UploadServlet.java

package com.zxy97.img.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class UploadServlet extends HttpServlet {

    static final String IMG_FOLDER = "img";//預設的存放img的根檔案夾
    /**
     * @描述 上傳資料及儲存檔案
     * @param request
     * @param response
     * @return 上傳結果
     */
    public static String upload(HttpServletRequest request, HttpServletResponse response) {
        try {
            //設定上傳的檔案大小
            final int MEMORY_THRESHOLD = 1024 * 1024 * 512;
            final int MAX_FILE_SIZE = 1024 * 1024 * 1024;
            final int MAX_REQUEST_SIZE = 1024 * 1024 * 1024;

            request.setCharacterEncoding("utf-8");
            response.setCharacterEncoding("utf-8");

            if (!ServletFileUpload.isMultipartContent(request)) {
                //檢測是否為多媒體上傳,如果不是則停止
                PrintWriter writer = response.getWriter();
                writer.println("錯誤:表單必須包含 enctype=multipart/form-data");
                writer.flush();
                return null;
            }
            DiskFileItemFactory factory = new DiskFileItemFactory();// 執行個體化,開始配置上傳參數
            factory.setSizeThreshold(MEMORY_THRESHOLD);// 設定記憶體臨界值 - 超過後将産生臨時檔案并存儲于臨時目錄中
            factory.setRepository(new File(System.getProperty("java.io.tmpdir")));// 設定臨時存儲目錄
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setFileSizeMax(MAX_FILE_SIZE);// 設定最大檔案上傳值
            upload.setSizeMax(MAX_REQUEST_SIZE);// 設定最大請求值 (包含檔案和表單資料)

            upload.setHeaderEncoding("UTF-8");// 防止中文亂碼

            // 解析請求的内容提取檔案資料
            List<FileItem> formItems = upload.parseRequest(request);

            if (formItems != null && formItems.size() > 0) {
                for (FileItem item : formItems) {
                    // 處理不在表單中的字段
                    if (!item.isFormField()) {
                        String itemName = item.getName();//擷取檔案名,進行驗證
                        if(checkFileName(itemName)){//驗證通過
                            java.util.Date date = new java.util.Date();//儲存當時的系統時間,也可以用别的命名方式
                            String path = "/" + IMG_FOLDER + new java.text.SimpleDateFormat("/yyyyMMddHHmmss").format(date) + getFileSuffix(itemName);//相對路徑
                            /*
                                推薦幾種:
                                SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss/")   在img檔案夾下建立多個檔案夾儲存檔案
                                SimpleDateFormat("/yyyyMMddHHmmss_")    在img檔案夾下将檔案名改成這種形式
                            */
                            
                            String filePath = request.getSession().getServletContext().getRealPath("/") + path;
                            
                            File file = new File(filePath);//要儲存的檔案
                            file.getParentFile().mkdirs();//為要儲存的檔案建立檔案夾,否則無法儲存
                            item.write(file);
                            return path;
                        }
                    }
                }
            }
            return null;
        }catch (Exception ex) {
            return null;
        }
    }
    
    /**
     * @描述 擷取檔案字尾名 
     */
    private static String getFileSuffix(final String fileName){
        int index = fileName.lastIndexOf(".");
        if(index >= 0){
            return fileName.substring(index);
        }
        return fileName;
    }
   
//     private String createFolder(String path){
//        File folder = new File(path);
//        if(!folder.exists()){
//            if(folder.mkdirs()){
//                return folder.getAbsolutePath();
//            }            
//        }
//        if(folder.isDirectory()){
//            return folder.getAbsolutePath();
//        }
//        return null;
//    }
     
    /**
     * @描述 允許上傳的檔案字尾
     */
    private static final String []arrSuffix = {
        ".jpg",".png",".bmp",".ico",".jpge",".gif"
    };
    
    public static String arrSuffixToString(){
        String str = "";
        for(String s:arrSuffix){
            str += s + " ";
            
        }
        return str;
    }
    
    /**
     * @描述 根據允許上傳的檔案字尾,進行檔案判斷 
     */
    private static boolean checkFileName(final String fileName){
        String name = fileName.toLowerCase();//轉小寫
        for(String suffix:arrSuffix){
            if(name.endsWith(suffix)){
                return true;
            }
        }
        return false;
    }

    /**
     * @描述 去掉Servlet帶來的備援路徑
     * 
     */
    private static String getURL(String requestURL){
        int index = requestURL.lastIndexOf("/");
        return requestURL.substring(0, index);
    }
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = upload(request, response);//上傳
        if(path == null){
            path = "上傳失敗!支援的檔案類型:" + arrSuffixToString() ;
        }else{
            path = getURL(request.getRequestURL().toString()) + path;
        }
        request.getSession().setAttribute("path", path);
        response.sendRedirect(getURL(request.getRequestURL().toString()));
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    public String getServletInfo() {
        return "Short description";
    }
}

                

5.2.3 建立

webroot/WEB-INF/web.xml

,用來配置servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.zxy97.img.servlet.UploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

           

5.2.4 建立

webroot/index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%
    String path =(String) session.getAttribute("path");
    if(path == null){
        path ="";
    }
%>
<!DOCTYPE html>
<html>
    <head>
        <title>zxy97_免費圖檔托管</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <h4><a href="http://zxy97.com/">Zxy97</a> | 圖檔托管網站</h4>
        <form action="upload" method="post" enctype="multipart/form-data">
            <input type="file" name="uploadfile" title="請選擇您的檔案">
            <button type="submit" class="btn">上傳檔案 </button>
        </form>
        <p><%=path %></p>
    </body>
</html>

                

6 實作一個資源托管網站

如果你已經成功地将圖檔外鍊網站跑起來了,那麼現在你可以對上面的那個項目稍加修改,動态地設定檔案擴充名,用來實作一個資源托管網站:http://cdn.zxy97.com。

你在src下寫的java代碼會編譯成class檔案存放到webroot/WEB-INF/classes下,釋出項目時隻需要打包webroot檔案夾,當時設定的檔案擴充名便無法修改了,是以你可以将檔案擴充名寫到配置檔案中,由程式讀取,到時候隻需要修改配置檔案即可設定網站的檔案上傳類型、表單最大值、檔案最大值、緩存最大值、儲存的檔案夾等等參數。

首先你需要在webroot/WEB-INF檔案夾下建立一個名為config.properties的配置檔案。

6.1 建立

webroot/WEB-INF/config.properties

目前webroot/WEB-INF/config.properties的内容隻有一行,就是用空格分隔的檔案擴充名:

suffixArray=.jpg .png .bmp .ico .jpge .gif .zip .rar .7z .mp3 .txt .html .xml
           

6.2 建立

com/zxy97/img/GetPath.java

package com.zxy97.img.servlet;


import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;

public class GetPath {
    
    public String[] getSuffixArray() throws IOException{
        String str = getValueByKey("suffixArray");
        return str.split(" ");
    }
    private String getValueByKey(String key) throws IOException{
        return getValueByKey(getWebRootPath()+"/WEB-INF/config.properties",key);
    }
    private String getValueByKey(String configFilePath, String key) throws FileNotFoundException, IOException{
        String value;
        Properties prop = new Properties();
        InputStream in = new BufferedInputStream (new FileInputStream(configFilePath));
        prop.load(in);
        value = prop.getProperty(key);
        return value;        
 
    }
    
   
    /**
     * @return WebRoot的絕對路徑,在src和jsp中通用的方法
     */
    public String getWebRootPath() {
        final String strClassName = getClass().getName();

        String strPackageName = "";
        if (getClass().getPackage() != null) {
            strPackageName = getClass().getPackage().getName();
        }

        String strClassFileName;
        if ("".equals(strPackageName)) {
            strClassFileName = strClassName;
        } else {
            strClassFileName = strClassName.substring(strPackageName.length() + 1, strClassName.length());
        }

        URL url = getClass().getResource(strClassFileName + ".class");

        String strURL = url.toString();
        strURL = strURL.substring(strURL.indexOf("/") + 1, strURL.lastIndexOf("WEB-INF"));
        return strURL;
    }
}

                

6.3 建立

com.zxy97.img/UploadServlet.java

package com.zxy97.img.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class UploadServlet extends HttpServlet {

    static final String IMG_FOLDER = "s";//預設的存放檔案的根檔案夾
    /**
     * @描述 上傳資料及儲存檔案
     * @param request
     * @param response
     * @return 上傳結果
     */
    public static String upload(HttpServletRequest request, HttpServletResponse response) {
        try {
            //設定上傳的檔案大小
            final int MEMORY_THRESHOLD = 1024 * 1024 * 512;
            final int MAX_FILE_SIZE = 1024 * 1024 * 1024;
            final int MAX_REQUEST_SIZE = 1024 * 1024 * 1024;

            request.setCharacterEncoding("utf-8");
            response.setCharacterEncoding("utf-8");

            if (!ServletFileUpload.isMultipartContent(request)) {
                //檢測是否為多媒體上傳,如果不是則停止
                PrintWriter writer = response.getWriter();
                writer.println("錯誤:表單必須包含 enctype=multipart/form-data");
                writer.flush();
                return null;
            }
            DiskFileItemFactory factory = new DiskFileItemFactory();// 執行個體化,開始配置上傳參數
            factory.setSizeThreshold(MEMORY_THRESHOLD);// 設定記憶體臨界值 - 超過後将産生臨時檔案并存儲于臨時目錄中
            factory.setRepository(new File(System.getProperty("java.io.tmpdir")));// 設定臨時存儲目錄
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setFileSizeMax(MAX_FILE_SIZE);// 設定最大檔案上傳值
            upload.setSizeMax(MAX_REQUEST_SIZE);// 設定最大請求值 (包含檔案和表單資料)

            upload.setHeaderEncoding("UTF-8");// 防止中文亂碼

            // 解析請求的内容提取檔案資料
            List<FileItem> formItems = upload.parseRequest(request);

            if (formItems != null && formItems.size() > 0) {
                for (FileItem item : formItems) {
                    // 處理不在表單中的字段
                    if (!item.isFormField()) {
                        String itemName = item.getName();//擷取檔案名,進行驗證
                        if(checkFileName(itemName)){//驗證通過
                            java.util.Date date = new java.util.Date();//儲存當時的系統時間,也可以用别的命名方式
                            String path = "/" + IMG_FOLDER + new java.text.SimpleDateFormat("/yyyyMMddHHmmss").format(date) + getFileSuffix(itemName);//相對路徑
                            /*
                                推薦幾種:
                                SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss/")   在img檔案夾下建立多個檔案夾儲存檔案
                                SimpleDateFormat("/yyyyMMddHHmmss_")    在img檔案夾下将檔案名改成這種形式
                            */
                            
                            String filePath = request.getSession().getServletContext().getRealPath("/") + path;
                            
                            File file = new File(filePath);//要儲存的檔案
                            file.getParentFile().mkdirs();//為要儲存的檔案建立檔案夾,否則無法儲存
                            item.write(file);
                            return path;
                        }
                    }
                }
            }
            return null;
        }catch (Exception ex) {
            return null;
        }
    }
    
    /**
     * @描述 擷取檔案字尾名 
     */
    private static String getFileSuffix(final String fileName){
        int index = fileName.lastIndexOf(".");
        if(index >= 0){
            return fileName.substring(index);
        }
        return fileName;
    }
   
//     private String createFolder(String path){
//        File folder = new File(path);
//        if(!folder.exists()){
//            if(folder.mkdirs()){
//                return folder.getAbsolutePath();
//            }            
//        }
//        if(folder.isDirectory()){
//            return folder.getAbsolutePath();
//        }
//        return null;
//    }
     
    /**
     * @描述 允許上傳的檔案字尾
     */
    private static String []arrSuffix;
    
    public static String arrSuffixToString(String []strs){
        String str = "";
        for(String s:strs){
            str += s + " ";
        }
        return str;
    }
    
    /**
     * @描述 根據允許上傳的檔案字尾,進行檔案判斷 
     */
    private static boolean checkFileName(final String fileName) throws IOException{
        String name = fileName.toLowerCase();//轉小寫
        arrSuffix = new GetPath().getSuffixArray();
 
        for(String suffix:arrSuffix){
            if(name.endsWith(suffix)){
                return true;
            }
        }
        return false;
    }

    /**
     * @描述 去掉Servlet帶來的備援路徑
     * 
     */
    private static String getURL(String requestURL){
        int index = requestURL.lastIndexOf("/");
        return requestURL.substring(0, index);
    }
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = upload(request, response);//上傳
        if(path == null){
            path = "上傳失敗!支援的檔案類型:" + arrSuffixToString(arrSuffix) ;
        }else{
            path = getURL(request.getRequestURL().toString()) + path;
        }
        request.getSession().setAttribute("path", path);
        response.sendRedirect(getURL(request.getRequestURL().toString()));
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    public String getServletInfo() {
        return "Short description";
    }
}

                

6.4 建立

webroot/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.zxy97.img.servlet.UploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

           

6.5 建立webroot/index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%
    String path =(String) session.getAttribute("path");
    if(path == null){
        path ="";
    }
%>
<!DOCTYPE html>
<html>
    <head>
        <title>zxy97_免費資源托管</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <h4><a href="http://zxy97.com/">Zxy97</a> | 資源托管網站</h4>
        <form action="upload" method="post" enctype="multipart/form-data">
            <input type="file" name="uploadfile" title="請選擇您的檔案">
            <button type="submit" class="btn">上傳檔案 </button>
        </form>
        <p><%=path %></p>
    </body>
</html>