文章目錄
- 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檔案上傳對頁面的要求
上傳檔案的要求比較多,需要記一下:
- 必須使用表單,而不能是超連結;
- 表單的method必須是POST,而不能是GET;
- 表單的enctype必須是multipart/form-data;
- 在表單中添加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屬性:
- 當
時,enctype的預設值為method="post"
,表示使用url編碼正文;application/x-www-form-urlencoded
- 當
時,enctype的預設值為method="get"
,沒有正文,是以就不需要enctype了。null
- 當
1.4 對普通文本表單的測試
通過httpWatch測試,檢視表單的請求資料正文,我們發現請求中隻有檔案名稱,而沒有檔案内容。也就是說,當表單的
enctype不是multipart/form-data
時,請求中不包含檔案内容,而隻有檔案的名稱,這說明普通文本表單中
input:file
與
input:text
沒什麼差別了。
測試-前端頁面
測試-請求正文
測試-請求頭
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包有:
-
,核心包;commons-fileupload.jar
-
,依賴包。commons-io.jar
下載下傳位址:http://cdn.zxy97.com/s/20180930194932.zip
2.2 fileupload簡單應用
fileupload的核心類有:DiskFileItemFactory、ServletFileUpload、FileItem。
使用fileupload元件的步驟如下:
- 建立工廠類DiskFileItemFactory對象:
- 使用工廠建立解析器對象:
- 使用解析器來解析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 項目結構
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
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.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
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>
5.2.4 建立 webroot/index.jsp
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
目前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
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
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
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>