Java Web 檔案上傳
阿新 • • 發佈:2021-07-19
檔案上傳
一、注意事項
- 為保證伺服器安全,上傳檔案應當儲存在外界無法直接訪問的路徑。(如WEB-INF目錄下)
- 為防止檔案覆蓋,要為上傳的檔案生成一個唯一的檔名。(如-時間戳,-uuid,-md5,-位運算演算法)
- 要限制上傳檔案的大小的最大值。
- 可以限制上傳檔案的型別,在獲取上傳檔名時,判斷後綴名是否合法。
二、元件
瀏覽器處理上傳檔案,是將檔案以流的形式提交到伺服器端。
- commons-fileupload:Apache的檔案上傳元件,取代原生的檔案上傳流。
- commons-io:commons-fileupload元件依賴於該元件。
三、前端表單
- form
- 提交方式:
method=“post”
- 編碼型別:
enctype="multipart/form-data"
(表單包含檔案上傳控制元件時必須使用)
- 提交方式:
- input
- 型別:
type=“file”
- 屬性:帶有name屬性
- 型別:
四、實用類介紹
PS:一個表單項即一個欄位,一個FileItem物件封裝了一個表單項
- DiskFileItemFactory類
// 1、設定臨時資料夾
void setRepository(File repository)
// *2、設定檔案快取區大小
void setSizeThreshold(int sizeThreshold)
- ServletFileUpload類
// 1、靜態方法:判斷表單是否包含檔案上傳控制元件,負責處理檔案資料 static boolean isMultipartContent(HttpServletRequest request) // 2、父類方法:設定FileItemFactory屬性,也可通過構造方法設定 void setFileItemFactory(FileItemFactory factory) // 3、解析前端請求,將每個表單項解析並封裝成FileItem物件,以List列表的形式返回。 List<FileItem> parseRequest(HttpServletRequest request) // *4、父類方法:監聽檔案上傳進度 void setProgressListener(ProgressListener pListener) // *5、父類方法:處理亂碼問題 void setHeaderEncoding(String encoding) // *6、父類方法:設定單個檔案的最大值 void setFileSizeMax(long fileSizeMax) // *7、父類方法:設定總共能夠上傳的檔案大小 void setSizeMax(long sizeMax)
- FileItem介面實現類:DiskFileItem
// 1、判斷表單項是否為上傳檔案:普通文字返回true,否則返回false
boolean isFormField();
// 2、獲取欄位名:表單項name屬性的值
String getFieldName();
// 3、FileItem物件中儲存的資料流內容:即表單項為普通文字的輸入值
String getString();
// 4、獲取表單項為檔案上傳的檔名
String getName();
// 5、獲取上傳檔案的輸入流
InputStream getInputStream();
// 6、清空FileItem類物件中存放的主體內容,如果主體內容被儲存在臨時檔案中,delete方法將刪除該臨時檔案
void delete();
五、程式碼實現
1、匯入Maven依賴
(普通專案則匯入jar包)
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
2、前端頁面
<h1>${msg}</h1>
<form action="${pageContext.request.contextPath}/upload.do" method="post" enctype="multipart/form-data">
<p>上傳使用者:<input type="text" name="username"></p>
<p><input type="file" name="file"></p>
<p>
<input type="submit">|<input type="reset">
</p>
</form>
3、Servlet
思路
- 判斷表單是否包含檔案上傳控制元件。
- 建立上傳檔案和臨時檔案的儲存路徑。
- 建立DiskFileItemFactory物件。
- 設定臨時資料夾
- *設定緩衝區大小
- 建立ServletFileUpload物件
- 設定FileItemFactory
- *監聽檔案上傳進度、處理亂碼問題、設定單個檔案和總共上傳檔案的最大值
- 解析請求並處理檔案傳輸
- 解析前端請求,將每個表單項解析並封裝成FileItem物件
- 判斷表單項是否為上傳檔案
- 處理普通文字:
- 獲取欄位名
- 獲取資料流內容
- 處理檔案:
- 獲取上傳檔名
- 生成隨機UUID作為檔案儲存路徑,併為其生成資料夾
- 通過IO流傳輸檔案
public class FileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 判斷表單是否包含檔案上傳控制元件
if (!ServletFileUpload.isMultipartContent(req)) { // 不包含檔案上傳控制元件,即普通表單
return;
}
// 建立上傳檔案的儲存路徑:外界無法直接訪問
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
makeDirIfNotExist(uploadFile);
// 建立臨時檔案的儲存路徑
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File tmpFile = new File(tmpPath);
makeDirIfNotExist(tmpFile);
// 1、建立DiskFileItemFactory物件
DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
// 2、建立ServletFileUpload物件
ServletFileUpload servletFileUpload = getServletFileUpload(factory);
// 3、解析請求並處理檔案傳輸
String msg = "";
try {
msg = uploadParseRequest(req, servletFileUpload, uploadPath);
} catch (FileUploadException e) {
e.printStackTrace();
}
req.setAttribute("msg", msg);
req.getRequestDispatcher("/index.jsp").forward(req, resp);
}
private String uploadParseRequest(HttpServletRequest httpServletRequest, ServletFileUpload servletFileUpload, String uploadPath)
throws FileUploadException, IOException {
String msg = "";
// 解析前端請求,將每個表單項解析並封裝成FileItem物件
List<FileItem> fileItems = servletFileUpload.parseRequest(httpServletRequest);
for (FileItem fileItem : fileItems) {
// 判斷表單項是否為上傳檔案
if (fileItem.isFormField()) { // 普通文字
String filedName = fileItem.getFieldName(); // 獲取欄位名:表單項name屬性的值
String value = fileItem.getString("UTF-8"); // FileItem物件中儲存的資料流內容:即表單項為普通文字的輸入值
System.out.println(filedName + ":" + value);
} else { // 上傳檔案
// ------------------------1、處理檔案------------------------
String uploadFileName = fileItem.getName();// 上傳檔名
System.out.println("上傳的檔名:" + uploadFileName);
if (uploadFileName == null || uploadFileName.trim().equals("")) { // 檔名為空值或空
continue; // 進入下一輪迴圈,判斷下一個FileItem物件
}
String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1); // 檔案字尾名
System.out.println("檔案資訊【檔名:" + uploadFileName + ",檔案型別:" + fileExtName + "】");
// 使用UUID保證檔名唯一
String uuidPath = UUID.randomUUID().toString();
// ------------------------2、處理路徑------------------------
// 儲存路徑:uploadPath
String realPath = uploadPath + '/' + uuidPath; // 真實存在的路徑
// 給每個檔案建立一個資料夾
File realPathFile = new File(realPath);
makeDirIfNotExist(realPathFile);
// ------------------------3、檔案傳輸------------------------
// 獲得輸入流
InputStream is = fileItem.getInputStream();
// 獲得輸出流
FileOutputStream fos = new FileOutputStream(realPathFile + "/" + uploadFileName);
// 建立緩衝區
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
msg = "檔案上傳成功!";
fileItem.delete(); // 上傳成功,清除臨時檔案
fos.close();
is.close();
}
}
return msg;
}
private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
// ServletFileUpload servletFileUpload = new ServletFileUpload(factory); // 構造方法設定FileItemFactory
ServletFileUpload servletFileUpload = new ServletFileUpload();
servletFileUpload.setFileItemFactory(factory); // 設定FileItemFactory
// ------------------------輔助功能------------------------
// 監聽檔案上傳進度
servletFileUpload.setProgressListener(new ProgressListener() {
/**
*
* @param pBytesRead 已讀取的檔案大小
* @param pContentLength 檔案大小
* @param pItems
*/
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
String percentage = (int) (((double) pBytesRead / pContentLength) * 100) + "%";
System.out.println("總大小:" + pContentLength + ",已上傳:" + pBytesRead + "\t" + percentage);
}
});
// 處理亂碼問題
servletFileUpload.setHeaderEncoding("UTF-8");
// 設定單個上傳檔案的最大值
servletFileUpload.setFileSizeMax(1024 * 1024 * 10); // 10M
// 設定總共上傳檔案的最大值
servletFileUpload.setSizeMax(1024 * 1024 * 10); // 10M
return servletFileUpload;
}
/**
* 獲得磁碟檔案專案工程,設定緩衝資料夾及緩衝區大小
*
* @param tmpFile 緩衝資料夾
* @return
*/
private DiskFileItemFactory getDiskFileItemFactory(File tmpFile) {
// return new DiskFileItemFactory(1024 * 1024, tmpFile);
DiskFileItemFactory factory = new DiskFileItemFactory();
// ------------------------輔助功能------------------------
factory.setSizeThreshold(1024 * 1024); // 1M(緩衝區大小):上傳檔案大於緩衝區大小時,fileupload元件將使用臨時檔案快取上傳檔案
factory.setRepository(tmpFile); // 臨時資料夾
return factory;
}
/**
* 如果檔案目錄不存在,為其建立目錄
*
* @param file
*/
private void makeDirIfNotExist(File file) {
if (!file.exists()) {
file.mkdir();
}
}
}
4、註冊Servlet
<servlet>
<servlet-name>FileServlet</servlet-name>
<servlet-class>indi.jaywee.file.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileServlet</servlet-name>
<url-pattern>/upload.do</url-pattern>
</servlet-mapping>
5、執行結果
上傳的檔案會儲存在Target目錄下對應專案的儲存路徑下。
-
非臨時檔案:
- 儲存在upload資料夾下。
- 檔案格式和檔名與上傳檔案完全相同。
- 每個檔案儲存在一個子資料夾中,資料夾名是隨機生成的UUID。
-
臨時檔案:
- 儲存在upload資料夾下,同時在tmp資料夾下生成一個臨時檔案。
- upload資料夾下的檔案:檔案格式和檔名與上傳檔案完全相同。
- tmp資料夾下的檔案:tmp格式,檔名是隨機生成的UUID,在Servlet執行到fileItem.delete()方法時被清除。