servlet實現上傳附件

一、簡單介紹

使用 Servlet上傳附件 原理上還是蠻簡單的,首先獲取上傳的附件對象,然後做一些簡單處理 後寫入到指定路徑的磁盤中。

二、準備條件

common-io.jar ,下載地址:http://commons.apache.org/io/download_io.cgi
common-upload.jar ,下載地址:http://jakarta.apache.org/commons/fileupload/

三、實現流程

首先在新建好的項目中 加入上述的兩個必須jar包,然後根據需求創建好upload.jsp頁面選擇附件,代碼如下:

<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Use servlet upload file</title>
</head>
<body>
<form name="uploadForm" method="post" action="servlet/uploadFileServlet" enctype="multipart/form-data">
    附件名稱:<input type="text" name="uploadName" value=""/><br/>
    選擇附件:<input type="file" name="uploadFile"/><br/>
    <input type="submit" value="上傳"/>
</form>
</body>
</html>

其中,enctype=“multipart/form-data” 是必須加入的,因爲原先沒有加入的普通表單是以字符提交到後臺的,但是加了之後所有參數全部會以二進制流格式傳入後臺,所以後臺處理代碼 request.getParameter(“paramName”) 將返回空字符串。

基本顯示如下圖:
在這裏插入圖片描述
頁面定義好了,接下來創建上面提交的servlet ,代碼如下:

package com.chenghui.servlet;
 
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
 
import javax.servlet.ServletConfig;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
 
/**
 * Servlet implementation class UploadFileServlet
 */
public class UploadFileServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    private static final String UPLOAD_PATH = "d:\\attach\\";
    
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadFileServlet() {
        super();
        // TODO Auto-generated constructor stub
    }
 
    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
    }
 
    /**
     * @see Servlet#destroy()
     */
    public void destroy() {
        // TODO Auto-generated method stub
    }
 
    /**
     * 
     * 偶然發現一個現象,當我定義的servlet 中重寫了 service方法,那麼每次調用的是service方法,除非註釋掉該方法纔會根據請求挑戰到對象的doPost/doGet中
     * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
     *//*
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("service");
    }*/
 
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        
        //創建一個磁盤文件的工廠,然後將它 傳遞到servletFileUplaod的實例中
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
        
        try {
            //根據request對象獲取所有的文件集合,這裏包括input標籤輸入的值也屬於FileInput
            List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
            Iterator iterator = fileItemList.iterator();
            String showFileName = "";
            while(iterator.hasNext()){
                FileItem fileItem = (FileItem)iterator.next();
                if(fileItem.isFormField()){ //是否是表單提交域,可以分區是否上傳的附件
                    String name = fileItem.getFieldName();  //input標籤的name
                    String value = fileItem.getString();    //input表單的value
                    if("uploadName".equals(name)){
                        showFileName = value;
                    }
                }else{
                    String fieldName = fileItem.getFieldName();  //表單提交過來的file input標籤中name的屬性值
                    String fileName = fileItem.getName();  //file input上傳的文件名
                    String contentType = fileItem.getContentType();  //獲得上傳文件的類型
                    long size = fileItem.getSize();      //上傳文件的大小
                    
                    String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
                    File saveFile = new File(filePath);
                    
                    fileItem.write(saveFile); //將文件寫入磁盤中
                    //成功之後 簡單輸出一下
                    PrintWriter out = response.getWriter();
                    out.print("上傳成功!上傳文件爲:"+fileName+"<br/>保存的地址爲"+filePath+ "!!");
                    out.close();
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

在web.xml中對應的配置:

  <servlet>
  	<servlet-name>uploadFileServlet</servlet-name>
  	<servlet-class>com.chenghui.servlet.UploadFileServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>uploadFileServlet</servlet-name>
  	<url-pattern>/servlet/uploadFileServlet</url-pattern>
  </servlet-mapping>

好了基本上可以走一下流程了,在upload.jsp頁面選擇附件後點擊“上傳”按鈕,效果顯示如下:
在這裏插入圖片描述然後去d:attach 目錄下找上傳的附件,找到了,ok上傳成功!但是顯示的文字有亂碼,這是因爲response對象在輸出的時候沒有指定具體的編碼,所以按照默認的iso-8859-1輸出 就亂碼了。

解決方案:在 PrintWriter out = response.getWriter(); 的上面加上

response.setContentType("text/html;charset=UTF-8");

就好了。

正確顯示效果如下:
在這裏插入圖片描述
另外,將上傳的附件是使用org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中的,這也可以用字節流完成該操作。

/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		//創建一個磁盤文件的工廠,然後將它 傳遞到servletFileUplaod的實例中
		DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
		ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
		
		try {
			//根據request對象獲取所有的文件集合,這裏包括input標籤輸入的值也屬於FileInput
			List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
			Iterator iterator = fileItemList.iterator();
			String showFileName = "";
			//如果附件地址不存在 就創建一下
			File uploadPath = new File(UPLOAD_PATH);
			if(!uploadPath.exists()){
				uploadPath.mkdir();
			}
			while(iterator.hasNext()){
				FileItem fileItem = (FileItem)iterator.next();
				if(fileItem.isFormField()){ //是否是表單提交域,可以分區是否上傳的附件
					String name = fileItem.getFieldName();  //input標籤的name
					String value = fileItem.getString();    //input表單的value
					if("uploadName".equals(name)){
						showFileName = value;
					}
				}else{
					String fieldName = fileItem.getFieldName();  //表單提交過來的file input標籤中name的屬性值
					String fileName = fileItem.getName();  //file input上傳的文件名
					String contentType = fileItem.getContentType();  //獲得上傳文件的類型
					long size = fileItem.getSize();      //上傳文件的笑答
					
					String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
					//org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中
					//fileItem.write(new File(filePath));
					//使用字節流讀取二進制格式的附件傳給文件流  然後 寫入磁盤
					OutputStream outputStream = new FileOutputStream(new File(UPLOAD_PATH,showFileName + fileName.substring(fileName.lastIndexOf("."))));//這裏傳遞父親的文件夾路徑和當前文件的名稱
					InputStream inputStream = fileItem.getInputStream();
					int length = 0;
					
					byte[] buf = new byte[1024];
					while((length = inputStream.read(buf)) != -1){  //首先根據傳遞的字節數組將讀取的字節的數量返回,在判斷是否讀取的空
						System.out.println(buf);
						outputStream.write(buf, 0, length);
					}
					inputStream.close();
					outputStream.close();
					//成功之後 簡單輸出一下
					response.setContentType("text/html;charset=UTF-8");
					PrintWriter out = response.getWriter();
                    out.print("上傳成功!上傳文件爲:"+fileName+"<br/>保存的地址爲"+filePath+ "!!");
                    out.close();
				}
			}
		} catch (FileUploadException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

上傳的效果是一樣的。輸出每次讀取的byte數組。
基本上,使用servlet 上傳附件的功能就已經完成了。

四、多附件上傳

如果有需求需要上傳多個附件,那麼現在這個demo同樣有效果,只需要稍作修改就好了。

upload.jsp需要增加一個選擇附件的控件,代碼 如下:

<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Use servlet upload file</title>
</head>
<body>
<form name="uploadForm" method="post" action="servlet/uploadFileServlet" enctype="multipart/form-data">
	附件名稱:<input type="text" name="uploadName" value=""/><br/>
	選擇附件:<input type="file" name="uploadFile"/><br/>
	
	附件名稱:<input type="text" name="uploadName1" value=""/><br/>
	選擇附件:<input type="file" name="uploadFile2"/><br/>
	
	<input type="submit" value="上傳"/>
</form>
</body>
</html>

servlet.java 邏輯其實不用改,因爲我們是迭代所有FileItem對象的集合,所以所有的附件和附件信息全都保存在這個集合中,簡單提煉了下代碼,具體如下:

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
 
		//創建一個磁盤文件的工廠,然後將它 傳遞到servletFileUplaod的實例中
		DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
		ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
		try {
			//根據request對象獲取所有的文件集合,這裏包括input標籤輸入的值也屬於FileInput
			List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
			Iterator iterator = fileItemList.iterator();
			String showFileName = "";
			String showFileName1 = "";
			//如果附件地址不存在 就創建一下
			File uploadPath = new File(UPLOAD_PATH);
			if(!uploadPath.exists()){
				uploadPath.mkdir();
			}
			while(iterator.hasNext()){
				FileItem fileItem = (FileItem)iterator.next();
				if(fileItem.isFormField()){ //是否是表單提交域,可以分區是否上傳的附件
					String name = fileItem.getFieldName();  //input標籤的name
					String value = fileItem.getString();    //input表單的value
					showFileName = value;  //這裏注意好出場順序,不然就亂套了
				}else{
					String fieldName = fileItem.getFieldName();  //表單提交過來的file input標籤中name的屬性值
					String fileName = fileItem.getName();  //file input上傳的文件名
					String contentType = fileItem.getContentType();  //獲得上傳文件的類型
					long size = fileItem.getSize();      //上傳文件的笑答
					
					String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
					//org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中
					//fileItem.write(new File(filePath));
					//使用字節流讀取二進制格式的附件傳給文件流  然後 寫入磁盤
					OutputStream outputStream = new FileOutputStream(new File(UPLOAD_PATH,showFileName + fileName.substring(fileName.lastIndexOf("."))));//這裏傳遞父親的文件夾路徑和當前文件的名稱
					InputStream inputStream = fileItem.getInputStream();
					int length = 0;
					
					byte[] buf = new byte[1024];
					while((length = inputStream.read(buf)) != -1){  //首先根據傳遞的字節數組將讀取的字節的數量返回,在判斷是否讀取的空
						System.out.println(buf);
						outputStream.write(buf, 0, length);
					}
					inputStream.close();
					outputStream.close();
					//成功之後 簡單輸出一下
                    out.print("上傳成功!上傳文件爲:"+fileName+"<br/>保存的地址爲"+filePath+ "!!");
				}
			}
		} catch (FileUploadException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			out.close();
		}
	}

五、實現下載功能

上傳功能實現完了 之後再來說說下載功能的具體實現。

如果我們把附件放在當前項目下,可以直接通過超鏈接 進行下載,但是假如附件放在我們上面例子的d:\attach 下那怎麼辦,tomcat 是不可能訪問項目以外的資源的。

所以接下來有兩種解決方案:

1、創建一個虛擬目錄

我們發佈項目的時候,也是通過在server.xml配置 實現將一個虛擬目錄發佈到tomcat服務器上,不一定這個虛擬目錄上非得有什麼WEB-INF,web.ml 等等之類的文件,這個目錄專門存放一些附件之類的同樣可以訪問。

那麼同理 在server.xml 中加上如下例子:

<Context docBase="D:\attach" path="/img" reloabable="true" />

訪問的話可以使用 localhost:8080/img/imgName 就好了。
2、採用輸入流讀取附件,然後輸出流寫入 即可完成下載功能

具體代碼如下:

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//獲取需要下載的附件的地址,然後採用輸入流InputStream讀取附件信息
		String filePath = request.getParameter("filePath");
		if(filePath!=null){
			try{
				File file = new File(filePath);
				//創建輸入流
				InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
				byte[] buffer = new byte[inputStream.available()];
				//傳遞available() 有效的字節數,可以一次性讀取完畢
				inputStream.read(buffer);
				inputStream.close();
				//清空response,設置response的Header
				response.reset();           
				response.addHeader("Content-Disposition", "attachment;filename=" + new String(file.getName().getBytes("utf-8"),"ISO-8859-1"));
	            response.addHeader("Content-Length", "" + file.length());
	            
				OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
				response.setContentType("application/octet-stream");
				outputStream.write(buffer);
				outputStream.flush();
				outputStream.close();
			} catch (IOException ex) {
	            ex.printStackTrace();
	        }
		}
	}

然後把上傳的輸出字符串改一下,加一個超鏈接,代碼如下:

//成功之後 簡單輸出一下
out.println("上傳成功!上傳文件爲:"+fileName+"<br/>保存的地址爲"+filePath+ "!!<a href='downloadFileServlet?filePath="+filePath+"'>下載</a><br/>");

接下來 重新部署下項目,看看結果。效果如下圖所示:
在這裏插入圖片描述ok,下載附件功能也完成了!

六、上傳附件中遇到問題的解決方案

1、中文亂碼

在上面的例子中 附件顯示名稱和 附件的名稱都是使用的英文名稱,如果改爲中文呢? 所以果斷嘗試了一下,結果都亂碼了,當時第一時間想到的是 使用request.setCharacterEncoding(“UTF-8”); 但是沒有效果呀,這種設置字符編碼 方式只適合普通表單的post請求。
後來發現 可以這樣,在創建ServletFileUpload對象之後設置一下頭部編碼,代碼如下:

ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
servletFileUpload.setHeaderEncoding("UTF-8");

再試一下,發現 附件的實際名稱沒變亂碼,但是 附件的顯示名稱還是亂碼,這需要單獨設置encoding,原先取參數都是

String value = fileItem.getString();    //input表單的value

需要傳遞encoding ,讓FileItem知道你想得到什麼格式的value,代碼如下:

String value = fileItem.getString("UTF-8");    //input表單的value

再重新部署一下,具體效果如下:
在這裏插入圖片描述
2、String類型的變量索引越界

String 類型的變量索引越界了,具體如下:

java.lang.StringIndexOutOfBoundsException: String index out of range: -1

仔細排查 發現如果上傳的附件框如果不選擇圖片,那麼Servlet還是會去按照常規獲取附件寫入磁盤,所以這裏

String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));

在它上面過濾掉就好了,具體如下:

   if(fileName.length()==0){
   	continue;
   }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章