導入相關支持jar包:commons-fileupload.jar,commons-io.jar
對於文件上傳,瀏覽器在上傳的過程中是將文件以流的形式提交到服務器端的,如果直接使用Servlet獲取上傳文件的輸入流然后再解析里面的請求參數是比較麻煩,所以一般選擇采用apache的開源工具common-fileupload這個文件上傳組件。這個common-fileupload上傳組件的jar包可以去apache官網上面下載,也可以在struts的lib文件夾下面找到,struts上傳的功能就是基於這個實現的。common-fileupload是依賴於common-io這個包的,所以還需要下載這個包。因為我是用的maven管理項目,所以要在pom文件中配置(每個人的jar包位置根據實際情況定)
XML代碼
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
實現文件上傳
文件上傳頁面和消息提示頁面
upload.jsp
<%--
Created by IntelliJ IDEA.
User: Eric Lan
Date: 2019/2/16
Time: 10:21
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上傳</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/Servlet/UploadhandleServlrt" enctype="multipart/form-data" method="post">
上傳用戶:<input type="text" name="username"> <br/>
上傳文件1:<input type="file" name="file1" id="file1"><br/>
上傳文件2:<input type="file" name="file2" id="file2"><br/>
<input type="submit" value="上傳">
</form>
</body>
</html>
message.jsp
<%--
Created by IntelliJ IDEA.
User: Eric Lan
Date: 2019/2/16
Time: 10:30
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>消息提示</title>
</head>
<body>
${message}
</body>
</html>
處理文件上傳的Servlet
package com.demo;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.UUID;
/**
* describe:
*
* @author Eric Lan
* @date 2019/02/16/10:34
*/
public class UploadHandleServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到上傳文件的保存目錄,將上傳的文件存放於WEB-INF目錄下,不允許外界直接訪問,保證上傳文件的安全
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
//上傳時生成的臨時文件保存目錄
String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
File tempfile = new File(tempPath);
if (!tempfile.exists() && !tempfile.isDirectory()) {
System.out.println(savePath + "目錄不存在,需要創建!");
//創建臨時目錄
tempfile.mkdir();
}
//消息提示
String message = "";
try {
//使用Apache文件上傳組件處理文件上傳步驟:
//1、創建一個DiskFileItemFactory工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
//設置工廠的緩沖區的大小,當上傳的文件大小超過緩沖區的大小時,就會生成一個臨時文件存放到指定的臨時目錄當中。
factory.setSizeThreshold(1024 * 100);
//設置上傳時生成的臨時文件的保存目錄
factory.setRepository(tempfile);
//2、創建一個文件上傳解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//監聽文件上傳進度
upload.setProgressListener(new ProgressListener() {
@Override
public void update(long l, long l1, int i) {
System.out.println("文件大小為:" + l1 + ",當前已處理:" + l);
}
});
//解決上傳文件名的中文亂碼
upload.setHeaderEncoding("UTF-8");
//3、判斷提交上來的數據是否是上傳表單的數據
if (!ServletFileUpload.isMultipartContent(req)) {
//按照傳統方式獲取數據
return;
}
//設置上傳單個文件的大小的最大值,目前是設置為1024*1024字節,也就是1MB
upload.setFileSizeMax(1024 * 1024);
//設置上傳文件總量的最大值,最大值=同時上傳的多個文件的大小的最大值的和,目前設置為10MB
upload.setSizeMax(1024 * 1024 * 10);
//4、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每一個FileItem對應一個Form表單的輸入項
List<FileItem> list = upload.parseRequest(req);
for (FileItem item : list) {
//如果fileitem中封裝的是普通輸入項的數據
if (item.isFormField()) {
String name = item.getFieldName();
//解決普通輸入項的數據的中文亂碼問題
String value = item.getString("UTF-8");
System.out.println(name + " = " + value);
} else {
//如果fileitem中封裝的是上傳文件
//得到上傳的文件名稱
String filename = item.getName();
System.out.println(filename);
if (filename == null || filename.trim().equals("")) {
continue;
}
filename = filename.substring(filename.lastIndexOf("\\") + 1);
//得到上傳文件的擴展名,如果需要限制上傳的文件類型,那么可以通過文件的擴展名來判斷上傳的文件類型是否合法
String fileTye = filename.substring(filename.lastIndexOf(".") + 1);
System.out.println("上傳文件的拓展名是: " + fileTye);
//獲取item中的上傳文件的輸入流
InputStream in = item.getInputStream();
//得到文件保存的名稱
String saveFileName = makeFileName(filename);
//得到文件的保存目錄
String realSavePath = makePath(saveFileName, savePath);
//創建一個文件輸出流
FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFileName);
//創建一個緩沖區
byte buffer[] = new byte[1024];
//判斷輸入流中的數據是否已經讀完的標識
int len = 0;
//循環將輸入流讀入到緩沖區當中,(len=in.read(buffer))>0就表示in里面還有數據
while ((len = in.read(buffer)) > 0) {
//使用FileOutputStream輸出流將緩沖區的數據寫入到指定的目錄(savePath + "\\" + filename)當中
out.write(buffer, 0, len);
}
//關閉輸入流
in.close();
//關閉輸出流
out.close();
//刪除處理文件上傳時生成的臨時文件
item.delete();
message = "文件上傳成功";
}
}
} catch (FileUploadBase.FileSizeLimitExceededException e) {
message = "單個文件超出最大值!!!";
e.printStackTrace();
req.setAttribute("message", message);
req.getRequestDispatcher("/message.jsp").forward(req, resp);
return;
} catch (FileUploadBase.SizeLimitExceededException e) {
message = "上傳文件的總的大小超出限制的最大值!!!";
e.printStackTrace();
req.setAttribute("message", message);
req.getRequestDispatcher("/message.jsp").forward(req, resp);
return;
} catch (Exception e) {
message = "文件上傳失敗!";
e.printStackTrace();
}
req.setAttribute("message", message);
req.getRequestDispatcher("/message.jsp").forward(req, resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
/**
* 生成上傳文件的文件名,文件名以:uuid+"_"+文件的原始名稱
* @param filename
* @return
*/
private String makeFileName(String filename) {
return UUID.randomUUID().toString() + "_" + filename;
}
/**
* 為防止一個目錄下面出現太多文件,要使用hash算法打散存儲
* @param filename
* @param filePath
* @return
*/
private String makePath(String filename, String filePath) {
int hashcode = filename.hashCode();
int dir1 = hashcode & 0xf;
int dir2 = (hashcode&0xf0) >> 4;
String dir = filePath + "\\" + dir1 + "\\" + dir2;
File file = new File(dir);
if (!file.exists()) {
file.mkdirs();
}
return dir;
}
}
文件上傳細節
1、為保證服務器安全,上傳文件應該放在外界無法直接訪問的目錄下,比如放於WEB-INF目錄下。
2、為防止文件覆蓋的現象發生,要為上傳文件產生一個唯一的文件名。
3、為防止一個目錄下面出現太多文件,要使用hash算法打散存儲。
4、要限制上傳文件的最大值。
5、要限制上傳文件的類型,在收到上傳文件名時,判斷后綴名是否合法。
在Web.xml文件中配置UploadHandleServlet
<servlet>
<servlet-name>UploadhandleServlrt</servlet-name>
<servlet-class>com.demo.UploadHandleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadhandleServlrt</servlet-name>
<url-pattern>/Servlet/UploadhandleServlrt</url-pattern>
</servlet-mapping>
文件下載
列出提供下載的文件資源
package com.demo;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* describe:
*
* @author Eric Lan
* @date 2019/02/16/16:27
*/
public class ListFileServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
Map<String, String> fileNameMap = new HashMap<String, String>();
listfile(new File(uploadFilePath), fileNameMap);
req.setAttribute("fileNameMap", fileNameMap);
req.getRequestDispatcher("/listfile.jsp").forward(req, resp);
}
public void listfile(File file, Map<String, String> map) {
if (!file.isFile()) {
File files[] = file.listFiles();
for (File f : files) {
listfile(f, map);
}
} else {
String realName = file.getName().substring(file.getName().indexOf("_") + 1);
map.put(file.getName(), realName);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
簡單說一下ListFileServlet中listfile方法,listfile方法是用來列出目錄下的所有文件的,listfile方法內部用到了遞歸,在實際開發當中,我們肯定會在數據庫創建一張表,里面會存儲上傳的文件名以及文件的具體存放目錄,我們通過查詢表就可以知道文件的具體存放目錄,是不需要用到遞歸操作的,這個例子是因為沒有使用數據庫存儲上傳的文件名和文件的具體存放位置,而上傳文件的存放位置又使用了散列算法打散存放,所以需要用到遞歸,在遞歸時,將獲取到的文件名存放到從外面傳遞到listfile方法里面的Map集合當中,這樣就可以保證所有的文件都存放在同一個Map集合當中。
實現文件下載
package com.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* describe:
*
* @author Eric Lan
* @date 2019/02/16/17:05
*/
public class DownLoadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//得到要下載的文件名
String fileName = request.getParameter("filename");
fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
//上傳的文件都是保存在/WEB-INF/upload目錄下的子目錄當中
String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload");
//通過文件名找出文件的所在目錄
String path = findFileSavePathByFileName(fileName,fileSaveRootPath);
//得到要下載的文件
File file = new File(path + "\\" + fileName);
//如果文件不存在
System.out.println(file);
if(!file.exists()){
request.setAttribute("message", "您要下載的資源已被刪除!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
//處理文件名
String realname = fileName.substring(fileName.indexOf("_")+1);
//設置響應頭,控制瀏覽器下載該文件
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));
//讀取要下載的文件,保存到文件輸入流
FileInputStream in = new FileInputStream(path + "\\" + fileName);
//創建輸出流
OutputStream out = response.getOutputStream();
//創建緩沖區
byte buffer[] = new byte[1024];
int len = 0;
//循環將輸入流中的內容讀取到緩沖區當中
while((len=in.read(buffer))>0){
//輸出緩沖區的內容到瀏覽器,實現文件下載
out.write(buffer, 0, len);
}
//關閉文件輸入流
in.close();
//關閉輸出流
out.close();
}
/**
* @Method: findFileSavePathByFileName
* @Description: 通過文件名和存儲上傳文件根目錄找出要下載的文件的所在路徑
* @param filename 要下載的文件名
* @param saveRootPath 上傳文件保存的根目錄,也就是/WEB-INF/upload目錄
* @return 要下載的文件的存儲目錄
*/
public String findFileSavePathByFileName(String filename,String saveRootPath){
int hashcode = filename.hashCode();
int dir1 = hashcode&0xf; //0--15
int dir2 = (hashcode&0xf0)>>4; //0-15
String dir = saveRootPath + "\\" + dir1 + "\\" + dir2;
File file = new File(dir);
if(!file.exists()){
//創建目錄
file.mkdirs();
}
return dir;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
展示下載文件的listfile.jsp頁面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE HTML>
<html>
<head>
<title>下載文件顯示頁面</title>
</head>
<body>
<!-- 遍歷Map集合 -->
<c:forEach var="me" items="${fileNameMap}">
<c:url value="/servlet/DownLoadServlet" var="downurl">
<c:param name="filename" value="${me.key}"></c:param>
</c:url>
${me.value}<a href="${downurl}">下載</a>
<br/>
</c:forEach>
</body>
</html>
注意Maven的pom.xml文件中需要導入的兩個C標簽需要的包!
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
在Web.xml文件中配置ListFileServlet和DownLoadServlet
<servlet>
<servlet-name>ListFileServlet</servlet-name>
<servlet-class>com.demo.ListFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ListFileServlet</servlet-name>
<url-pattern>/servlet/ListFileServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DownLoadServlet</servlet-name>
<servlet-class>com.demo.DownLoadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownLoadServlet</servlet-name>
<url-pattern>/servlet/DownLoadServlet</url-pattern>
</servlet-mapping>
至此,JavaWeb中的文件上傳和下載功能的內容大概就這么多。