1. 程式人生 > >java-web檔案上傳下載,可解決多個安全訪問問題

java-web檔案上傳下載,可解決多個安全訪問問題

檔案上傳下載,可解決多個安全訪問問題。這個是上年老師教給我們的一個方法,這裡當做筆記記錄一下。

說明:對於檔案上傳,瀏覽器在上傳的過程中是將檔案以流的形式提交到伺服器端的,如果直接使用Servlet獲取上傳檔案的輸入流然後再解析裡面的請求引數是比較麻煩,所以一般選擇採用apache的開源工具common-fileupload這個檔案上傳元件。這個common-fileupload上傳元件的jar包可以去apache官網上面下載,也可以在struts的lib資料夾下面找到,struts上傳的功能就是基於這個實現的。common-fileupload是依賴於common-io這個包的,所以還需要下載這個包。

下面是搭建步驟:

一、新增2個包:

commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar

二、檔案上傳

2.1、檔案上傳jsp頁面:upload.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>  
    <title>My JSP 'upload.jsp' starting page</title>   
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/servlet/UploadServlet" 
               enctype="multipart/form-data" method="post">
    	上傳使用者:<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>
  </body>
</html>

2.2、處理檔案上傳的Servlet:UploadServlet

檔案上傳的細節

1.  為保證伺服器安全,上傳檔案應該放在外界無法直接訪問的目錄下,比如放於WEB-INF目錄下

2.  為防止檔案覆蓋的現象發生,要為上傳檔案產生一個唯一的檔名。

3.  為防止一個目錄下面出現太多檔案,要使用hash演算法打散儲存

4.  要限制上傳檔案的最大值。

5. 要限制上傳檔案的型別,在收到上傳檔名時,判斷後綴名是否合法。

針對上述提出的5點細節問題,我們需要:

1、為了不讓外界訪問我們的上傳的檔案資源,我們把上傳資源放到WEB-INF目錄下。

2、可以使用MD5演算法來生成唯一字串,把該字串和上傳檔名使用“_”(英文下劃線)連線起來組成我們的最終的檔名稱,以確保該上傳檔案的唯一性。

/**
* @Method: makeFileName
* @Description: 生成上傳檔案的檔名,檔名以:uuid+"_"+檔案的原始名稱
* @param filename 檔案的原始名稱
* @return uuid+"_"+檔案的原始名稱
*/ 
private String makeFileName(String filename){  //2.jpg
	 //為防止檔案覆蓋的現象發生,要為上傳檔案產生一個唯一的檔名
	 return UUID.randomUUID().toString() + "_" + filename;
}

3、我們使用一下方式打散我們的的上傳檔案,使用分別在多個不同目錄中

/**
* 為防止一個目錄下面出現太多檔案,要使用hash演算法打散儲存
* @Method: makePath
* @Description: 
* @param filename 檔名,要根據檔名生成儲存目錄
* @param savePath 檔案儲存路徑
* @return 新的儲存目錄
*/
private String makePath(String filename,String savePath){
	//得到檔名的hashCode的值,得到的就是filename這個字串物件在記憶體中的地址
	int hashcode = filename.hashCode();
	int dir1 = hashcode&0xf;  //0-15
	int dir2 = (hashcode&0xf0)>>4;  //0-15
	//構造新的儲存目錄
	String dir = savePath + "\\" + dir1 + "\\" + dir2;  //upload\2\3  upload\3\5
	//File既可以代表檔案也可以代表目錄
	File file = new File(dir);
	//如果目錄不存在
	if(!file.exists()){
		//建立目錄
		file.mkdirs();
	}
	return dir;
}

4、我們設定以下物件的屬性來限制單個上傳檔案的最大值和單次上傳的總檔案大小

ServletFileUpload upload = new ServletFileUpload(factory);
//設定上傳單個檔案的大小的最大值,目前是設定為1024*1024位元組,也就是1MB
upload.setFileSizeMax(1024*1024);
//設定上傳檔案總量的最大值,最大值=同時上傳的多個檔案的大小的最大值的和,目前設定為10MB
upload.setSizeMax(1024*1024*10);

5、我們通過上傳檔案的資訊可以獲取到檔案全名稱,從全名稱擷取字尾名即可判斷檔案型別

//得到上傳的檔名稱
//注意:不同的瀏覽器提交的檔名是不一樣的,有些瀏覽器提交上來的檔名是帶有路徑的,如:c:\a\b\1.txt,而有些只是單純的檔名,如:1.txt
String fileFullName = item.getName();
System.out.println(fileFullName);
String fileName = fileFullName.substring(fileFullName.lastIndexOf("\\")+1);
//得到上傳檔案的副檔名
String fileExtName = fileName.substring(fileName.lastIndexOf(".")+1);
//如果需要限制上傳的檔案型別,那麼可以通過檔案的副檔名來判斷上傳的檔案型別是否合法
System.out.println("上傳的檔案的副檔名是:"+fileExtName);

全部 UploadServlet 程式碼:


package com.gx.servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.UUID;

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.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String message = null;
		try {
			 //使用Apache檔案上傳元件處理檔案上傳步驟:
			//1、建立一個DiskFileItemFactory工廠
			DiskFileItemFactory factory = new DiskFileItemFactory();
			
			//2、建立一個檔案上傳解析器
			ServletFileUpload upload = new ServletFileUpload(factory);
			//解決上傳檔名的中文亂碼
			upload.setHeaderEncoding("UTF-8");
			//3.判斷上傳表單的型別(判斷提交上來的資料是否是上傳表單的資料)
			if(!upload.isMultipartContent(request)){
				//上傳表單為普通表單,則按照傳統方式獲取資料即可
				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(request);
			for(FileItem item : list){
				//如果fileitem中封裝的是普通輸入項的資料
				if(item.isFormField()){
					String name = item.getFieldName();
					String value = item.getString();
					System.out.println("name="+name+",value="+value);
				}else{//如果fileitem中封裝的是上傳檔案
					//得到上傳的檔名稱
					//注意:不同的瀏覽器提交的檔名是不一樣的,有些瀏覽器提交上來的檔名是帶有路徑的,如:  c:\a\b\1.txt,而有些只是單純的檔名,如:1.txt
					String fileFullName = item.getName();
					String fileName = fileFullName.substring(fileFullName.lastIndexOf("\\")+1);
					//得到上傳檔案的副檔名
					String fileExtName = fileName.substring(fileName.lastIndexOf(".")+1);
					//如果需要限制上傳的檔案型別,那麼可以通過檔案的副檔名來判斷上傳的檔案型別是否合法
					System.out.println("上傳的檔案的副檔名是:"+fileExtName);
					
					//獲取檔案儲存的路徑
					String savePath = this.getServletContext().getRealPath("/upload");
					//得到檔案儲存的名稱
					String saveFileName = makeFileName(fileName);
					//得到檔案的儲存目錄
					String realSavePath = makePath(saveFileName, savePath);
					InputStream in = item.getInputStream();
					byte [] buffer = new byte[1024];
					int len = 0;
					OutputStream os= new FileOutputStream(realSavePath+File.separator+saveFileName);
					while((len=in.read(buffer))!=-1){
						os.write(buffer, 0, len);
					}
					in.close();
					os.close();
				}
			}
		} catch (FileUploadBase.FileSizeLimitExceededException e) {
				e.printStackTrace();
				request.setAttribute("message", "單個檔案超出最大值!!!");
				request.getRequestDispatcher("/message.jsp").forward(request, response);
				return;
			}catch (FileUploadBase.SizeLimitExceededException e) {
				e.printStackTrace();
				request.setAttribute("message", "上傳檔案的總的大小超出限制的最大值!!!");
			    request.getRequestDispatcher("/message.jsp").forward(request, response);
				 return;
			}catch (Exception e) {
				message= "檔案上傳失敗!";
				e.printStackTrace();
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
	
	/**
	* @Method: makeFileName
	* @Description: 生成上傳檔案的檔名,檔名以:uuid+"_"+檔案的原始名稱
	* @param filename 檔案的原始名稱
	* @return uuid+"_"+檔案的原始名稱
	 */ 
	private String makeFileName(String filename){  //2.jpg
		 //為防止檔案覆蓋的現象發生,要為上傳檔案產生一個唯一的檔名
		 return UUID.randomUUID().toString() + "_" + filename;
	}
	
	 /**
	* 為防止一個目錄下面出現太多檔案,要使用hash演算法打散儲存
	* @Method: makePath
	* @Description: 
	* @param filename 檔名,要根據檔名生成儲存目錄
	* @param savePath 檔案儲存路徑
	* @return 新的儲存目錄
	*/
	private String makePath(String filename,String savePath){
		//得到檔名的hashCode的值,得到的就是filename這個字串物件在記憶體中的地址
		int hashcode = filename.hashCode();
		int dir1 = hashcode&0xf;  //0-15
		int dir2 = (hashcode&0xf0)>>4;  //0-15
		//構造新的儲存目錄
		String dir = savePath + "\\" + dir1 + "\\" + dir2;  //upload\2\3  upload\3\5
		//File既可以代表檔案也可以代表目錄
		File file = new File(dir);
		//如果目錄不存在
		if(!file.exists()){
			//建立目錄
			file.mkdirs();
		}
		 return dir;
		}

}
註釋有點多,程式碼看起來有點亂了,複製看看吧。

2.3、在Web.xml檔案中註冊UploadServlet  

<servlet>

    <servlet-name>UploadServlet</servlet-name>

    <servlet-class>com.gx.servlet.UploadServlet</servlet-class>

  </servlet>

  <servlet-mapping>

    <servlet-name>UploadServlet</servlet-name>

    <url-pattern>/servlet/UploadServlet</url-pattern>

  </servlet-mapping>

三、檔案下載

3.1、檔案上傳jsp頁面:download.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'download.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
  </head>
  <body>
    <a href="${pageContext.request.contextPath}/servlet/DownloadServlet?fileName=718b8f7b-c0e2-45b1-8a36-a271b341cf25_中國.jpg">下載</a>
  </body>
</html>

fileName=..._中國.jpg這個是上面上傳的圖片,自己要對應改

3.2、處理檔案下載的servlet:DownloadServlet

package com.gx.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sun.org.apache.xml.internal.security.utils.Base64;

public class DownloadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
		//獲得要下載的檔名
		String fileName = request.getParameter("fileName");
		fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
		//上傳的檔案都是儲存在/upload目錄下的子目錄當中
		String fileSaveRootPath=this.getServletContext().getRealPath("/upload");
		//通過檔名找出檔案的所在目錄
		String path = findFileSavePathByFileName(fileName,fileSaveRootPath);
		//得到要下載的檔案
		File file = new File(path,fileName);
		System.out.println("檔案儲存路徑:"+path+"\\"+fileName);
		//如果檔案不存在
		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", String.format("attachment;filename*=utf-8’zh_cn%s",java.net.URLEncoder.encode(realName,"UTF-8")));
		//讀取要下載的檔案,儲存到檔案輸入流
		FileInputStream in = new FileInputStream(file);
		//建立輸出流
		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();
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

		/**
		* @Method: findFileSavePathByFileName
		* @Description: 通過檔名和儲存上傳檔案根目錄找出要下載的檔案的所在路徑
		* @param filename 要下載的檔名
		* @param saveRootPath 上傳檔案儲存的根目錄,也就是/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;  //upload\2\3  upload\3\5
			File file = new File(dir);
			if(!file.exists()){
			    //建立目錄
			    file.mkdirs();
			}
			return dir;
		}
}

3.3、在Web.xml檔案中註冊DownloadServlet

<servlet>
    <servlet-name>DownloadServlet</servlet-name>
    <servlet-class>com.gx.servlet.DownloadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DownloadServlet</servlet-name>
    <url-pattern>/servlet/DownloadServlet</url-pattern>
  </servlet-mapping>


以上就是檔案上傳下載功能簡單實現了。

人不要lian,天下無敵。求點個贊奮鬥