檔案的上傳與下載

smlxs_lbb發表於2018-04-09

[TOC]

檔案上傳技術

  • Servlet3.0
  • JSPSmartUpload:嵌入到JSP中完成檔案上傳.主要用於Model1年代的.
  • FileUpload:Apache的檔案上傳元件.
  • Struts2:底層是FileUpload.

檔案上傳的要素

  • 表單的提交的方式必須是POST.
  • 表單中需要有檔案上傳的表單元素:這個元素這個元素必須有name屬性和值<input type=”file” name=”upload”>
  • 表單的enctype屬性的值必須是multipart/form-data.

一、檔案上傳原理分析

沒有設定enctype屬性的時候:只能獲得檔案的名稱,而沒有檔案內容,如下圖

這裡寫圖片描述
設定enctype屬性為multipart/form-data:獲得到檔名及檔案內容,如下圖
這裡寫圖片描述
檔案上傳的原理圖
這裡寫圖片描述

二、上傳方式

使用Servlet3.0實現檔案上傳

程式碼如下

<body>
<h1>檔案上傳的頁面</h1>
<!-- 
	* 表單的提交的方式必須是POST.
	* 表單中需要有檔案上傳的表單元素:這個元素這個元素必須有name屬性和值:<input type=”file” name=”upload”>
	* 表單的enctype屬性的值必須是multipart/form-data.
 -->
<form action="${ pageContext.request.contextPath }/UploadServlet" method="post" enctype="multipart/form-data">
	<table border="1" width="600">
		<tr>
			<td>檔案描述</td>
			<td><input type="text" name="filedesc"></td>
		</tr>
		<tr>
			<td>檔案上傳</td>
			<td><input type="file" name="upload"></td>
		</tr>
		<tr>
			<td colspan="2"><input type="submit" value="上傳"></td>
		</tr>
	</table>
</form>
</body>
複製程式碼
/**
 * 檔案上傳的Servlet
 */
@WebServlet("/UploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 接收普通資料:
		request.setCharacterEncoding("UTF-8");
		String filedesc = request.getParameter("filedesc");
		System.out.println("檔案描述"+filedesc);
		// 接收檔案:
		Part part = request.getPart("upload");
		
		long size = part.getSize();// 獲得檔案大小:
		System.out.println("檔案大小:"+size);
		String name = part.getName();
		System.out.println("檔案表單中的name屬性的名稱"+name);
		// 獲得檔名:
		String header = part.getHeader("Content-Disposition");
		int idx = header.lastIndexOf("filename=\"");
		String fileName = header.substring(idx+10, header.length()-1);
		//IE瀏覽器不行,加上以下程式碼
		//String[] split = fileName.split("//");
		//fileName = split[split.length-1];
		System.out.println("檔名:"+fileName);
		// 獲得檔案內容:
		InputStream is = part.getInputStream();
		// 獲得upload的路徑:
		//專案根路徑下不安全,最好存到web-inf目錄下
		String path = this.getServletContext().getRealPath("/WEB-INF/upload");
		// 獲得檔案的唯一檔名:
		String uuidFileName = UUIDUtils.getUUIDFileName(fileName);
		String realPath = path+UploadUtils.getPath(uuidFileName);
		File file = new File(realPath);
		if(!file.exists()){
			file.mkdirs();
		}
		OutputStream os = new FileOutputStream(realPath+"/"+uuidFileName);
		byte[] b = new byte[1024];
		int len = 0;
		while((len = is.read(b))!=-1){
			os.write(b, 0, len);
		}
		is.close();
		os.close();
	}
複製程式碼

使用fileUpload實現檔案上傳

/*	
	 檔案的上傳:
	     * 三個要素:
	         * 提交的方式是POST:
	         * 表單中需要有<input type=”file” name=”upload”>
	         * enctype=”multipart/form-data”設定後request.getParamater()就不能用了。

	     * 檔案上傳的技術:
	         * Servlet3.0
	         * JSPSmartUpload
	         * FileUpload:			上傳檔案大小有限制
	             * commons-fileupload-1.2.1.jar
	             * commons-io-1.4.jar
	         * Struts2

	     * 使用FileUpload的時候:
	         * 獲得磁碟檔案工廠物件:
	         * 通過工廠獲得核心解析類:
	         * 解析request物件 , 返回集合,集合中的內容是分割線分成的每個部分.
	         * 遍歷每個部分:
*/	        	 
	      

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		try {
			// 建立一個Product的物件:
			Product product = new Product();
			// 建立磁碟檔案項工廠.
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			// 設定快取區的大小: 如果檔案的大小超過了緩衝區的大小,就會產生臨時檔案.
			diskFileItemFactory.setSizeThreshold(3 * 1024 * 1024);
			// 設定臨時檔案存放的路徑:
			// diskFileItemFactory.setRepository(repository);
			// 獲得核心解析類:ServletFileUpload
			ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
			fileUpload.setHeaderEncoding("UTF-8");// 解決中文檔名上傳亂碼.
			// fileUpload.setFileSizeMax(fileSizeMax); // 設定單個檔案大小
			// fileUpload.setSizeMax(sizeMax); // 設定表單中的所有檔案項的檔案總大小
			// 解析request 返回List集合
			List<FileItem> list = fileUpload.parseRequest(request);
			// 獲得每個部分:
			// 將遍歷的值存入到一個Map集合中:
			Map<String,String> map = new HashMap<String,String>();
			String fileName=null;
			for (FileItem fileItem : list) {
				//&emsp;判斷普通項和檔案上傳項:
				if(fileItem.isFormField()){
					// 普通項
					String name = fileItem.getFieldName();
					String value = fileItem.getString("UTF-8"); // 解決的是普通項的中文亂碼.
					System.out.println(name+"   "+value);
					map.put(name, value);
				}else{
					// 檔案上傳項 
					// 獲得檔名:
					fileName = fileItem.getName();
					System.out.println("檔名:"+fileName);
					// 獲得檔案的輸入流:
					InputStream is = fileItem.getInputStream();
					// 獲得檔案要上傳的路徑:
					String path = this.getServletContext().getRealPath("/products/1");
					OutputStream os = new FileOutputStream(path+"/"+fileName);
					int length = 0;
					byte[] b= new byte[1024];
					while((length = is.read(b))!=-1){
						os.write(b, 0, length);
					}
					is.close();
					os.close();
				}
			}
			// 封裝資料:
			BeanUtils.populate(product, map);
			product.setPid(UUIDUtils.getUUID());
			product.setPdate(new Date());
			product.setPflag(0);
			product.setPimage("products/1/"+fileName);
			Category category = new Category();
			category.setCid(map.get("cid"));
			product.setCategory(category);
			
			// 存入到資料庫:
			ProductService productService = (ProductService) BeanFactory.getBean("productService");
			productService.save(product);
			
			// 頁面跳轉:
			response.sendRedirect(request.getContextPath()+"/AdminProductServlet?method=findByPage&currPage=1");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
複製程式碼

Struts2檔案上傳

瀏覽器端注意事項

表單提交方式method=post 表單中必須有一個元件 表單中必須設定enctype=”multipart/form-data”

伺服器端

Commons-fileupoad.jar包完成。

Struts2框架使用一個fileupload的interceptor來完成檔案上傳

上傳案例

<form action="${ pageContext.request.contextPath }/upMany" enctype="multipart/form-data" method="post">
<input type="file" name="upload">
<input type="file" name="upload">
<input type="submit" value="上傳">
</form>
複製程式碼
public class UploadActionMany extends ActionSupport{
//	 extends ActionSupport 
	private File[] upload;
	private String[] uploadContentType;
	private String[] uploadFileName;
/*
	提供對應的set/get方法
*/
	public String upload(){
		System.out.println(3);
		String path = ServletActionContext.getServletContext().getRealPath("/upload");
		try {
			for (int i = 0; i < upload.length; i++) {
				File file = new File(path,uploadFileName[i]);
				FileUtils.copyFile(upload[i], file);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(4);
		return null;
	}
}
複製程式碼

注意事項

<!-- 開發模式 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 設定檔案上傳的大小限制 -->
<constant name="struts.multipart.maxSize" value="40971520"></constant>
<action name="upMany" class="com.lbb.struts2.action.UploadActionMany" method="upload">
	<!-- 檔案上傳出錯後的檢視 -->
	<result name="input">/error.jsp</result>
	<interceptor-ref name="fileUpload">
		<!-- <param name="maximumSize"></param> --><!-- 設定每一個檔案的單獨的上傳大小 -->
		<!-- <param name="allowedTypes"></param> --><!-- 檔案的mime型別 -->
		<param name="allowedExtensions">txt,jpg,bmp</param><!-- 設定允許的字尾名 -->
	</interceptor-ref>
	<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
複製程式碼

input接受錯誤資訊

<s:actionerror/>
<s:fielderror/>
複製程式碼

三、檔案上傳問題

檔案重名

使用UUID生成隨機的唯一檔名

同一目錄下檔案過多

一個目錄下檔案過多,導致開啟都很慢,更別說是讀寫操作. 目錄分離方案

  • 按時間分:一個月一個目錄,一個星期一個目錄,一天一個目錄
  • 按數量分:一個目錄下存5000個檔案,建立一個新的目錄,再去存放
  • 按使用者分:為每個使用者建立一個單獨目錄 存放檔案
  • 按目錄分離演算法分 使用唯一檔名.hashCode(); -- 得到一個代表當前這個檔案的int型別值. int型別佔4個位元組32位.可以讓hashCode值&0xf; 得到一個int值,用這個int值作為一級目錄. 讓hashCode右移4位 &0xf ;得到一個int值,用這個int值作為二級目錄.依次類推.
public class UploadUtils {

	public static String getPath(String uuidFileName){
		// 使用唯一檔名.hashCode();
		int code1 = uuidFileName.hashCode();
		int d1 = code1 & 0xf; // 獲得到1級目錄.
		int code2 = code1 >>> 4;
		int d2 = code2 & 0xf; // 獲得到2級目錄.
		return "/"+d1+"/"+d2;
	}
}
複製程式碼

原理圖如下

這裡寫圖片描述

四、檔案下載

檔案下載的方式

一種:超連結下載.直接將檔案的路徑寫到超連結的href中.---前提:檔案型別,瀏覽器不支援. 二種:手動編寫程式碼的方式完成檔案的下載. 設定兩個頭和一個流: Content-Type:檔案的MIME的型別. Content-Disposition :以下載的形式開啟檔案. InputStream:檔案的輸入流.

<body>
<h1>檔案下載的列表頁面</h1>
<h3>超連結的下載</h3>
<a href="/day10/download/hello.txt">hello.txt</a><br/>
<a href="/day10/download/cs10001.jpg">cs10001.jpg</a><br/>
<a href="/day10/download/hello.zip">hello.zip</a><br/>

<h3>手動編碼方式下載</h3>
<a href="/day10/DownloadServlet?filename=hello.txt">hello.txt</a><br/>
<a href="/day10/DownloadServlet?filename=cs10001.jpg">cs10001.jpg</a><br/>
<a href="/day10/DownloadServlet?filename=hello.zip">hello.zip</a><br/>
<a href="/day10/DownloadServlet?filename=美女.jpg">美女.jpg</a><br/>
</body>
複製程式碼
IE瀏覽器下載中文檔案的時候採用的URL的編碼.
Firefox瀏覽器下載中文檔案的時候採用的是Base64的編碼.
/**
 * 檔案下載的Servlet
 */
public class DownloadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 1.接收引數
		String filename = new String(request.getParameter("filename").getBytes("ISO-8859-1"),"UTF-8");
		System.out.println(filename);
		// 2.完成檔案下載:
		// 2.1設定Content-Type頭
		String type = this.getServletContext().getMimeType(filename);
		response.setHeader("Content-Type", type);
		// 2.3設定檔案的InputStream.
		String realPath = this.getServletContext().getRealPath("/download/"+filename);
		
		// 根據瀏覽器的型別處理中文檔案的亂碼問題:
		String agent = request.getHeader("User-Agent");
		System.out.println(agent);
		if(agent.contains("Firefox")){
			filename = base64EncodeFileName(filename);
		}else{
			filename = URLEncoder.encode(filename,"UTF-8");
		}
		
		// 2.2設定Content-Disposition頭
		response.setHeader("Content-Disposition", "attachment;filename="+filename);
		
		InputStream is = new FileInputStream(realPath);
		// 獲得response的輸出流:
		OutputStream os = response.getOutputStream();
		int len = 0;
		byte[] b = new byte[1024];
		while((len = is.read(b))!= -1){
			os.write(b, 0, len);
		}
		is.close();
	}
	
	public static String base64EncodeFileName(String fileName) {
		BASE64Encoder base64Encoder = new BASE64Encoder();
		try {
			return "=?UTF-8?B?"
					+ new String(base64Encoder.encode(fileName
							.getBytes("UTF-8"))) + "?=";
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
複製程式碼

相關文章