IT兄弟連 JavaWeb教程 文件上傳技術

在Web應用系統開發中,文件上傳和下載功能是很是經常使用的功能。html

對於文件上傳,瀏覽器在上傳的過程當中是將文件以流的形式提交到服務器端的,若是直接使用Servlet獲取上傳文件的輸入流而後再解析裏面的請求參數是比較麻煩,因此通常選擇採用apache的開源工具common-fileupload這個文件上傳組件。這個common-fileupload上傳組件的jar包能夠去apache官網上面下載。common-fileupload是依賴於common-io這個包的,因此還須要下載這個包。java

 

開發環境搭建web

建立一個FileUploadAndDownload項目,加入Apache的commons-fileupload文件上傳組件的相關Jar包,如圖20所示。算法

6c8ca398701946b69c31787994edcd18.png

圖20  導入Jar包apache

 

實現文件上傳瀏覽器

●  編寫文件上傳頁面,upload.jsp頁面代碼以下:安全

<%@ page language="java" contentType="text/html; charset=UTF-8"服務器

    pageEncoding="UTF-8"%>app

<!DOCTYPE html>dom

<html>

<head>

<meta charset="UTF-8">

<title>兄弟連IT教育</title>

</head>

<body>

    <form action="${pageContext.request.contextPath}/upload"

        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>

●  編寫消息提示頁面,message.jsp頁面代碼以下:

<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>兄弟連IT教育</title>

</head>

<body>

    ${message}

</body>

</html>

●  編寫處理文件上傳的Servlet

package com.xdl.servlet;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.List;

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.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    public void service(HttpServletRequest request,

        HttpServletResponse response) throws ServletException, IOException {

        // 獲得上傳文件的保存目錄,將上傳的文件存放於WEB-INF目錄下,

        // 不容許外界直接訪問,保證上傳文件的安全

        String savePath = this.getServletContext().

                             getRealPath("/WEB-INF/ upload");

        File file = new File(savePath);

        // 判斷上傳文件的保存目錄是否存在

        if (!file.exists() && !file.isDirectory()) {

             System.out.println(savePath + "目錄不存在,須要建立");

             // 建立目錄

             file.mkdir();

        }

        // 消息提示

        String message = "";

        try {

             // 使用Apache文件上傳組件處理文件上傳步驟:

             // 一、建立一個DiskFileItemFactory工廠

             DiskFileItemFactory factory = new DiskFileItemFactory();

             // 二、建立一個文件上傳解析器

             ServletFileUpload upload = new ServletFileUpload(factory);

             // 解決上傳文件名的中文亂碼

             upload.setHeaderEncoding("UTF-8");

             // 三、判斷提交上來的數據是不是上傳表單的數據

             if (!ServletFileUpload.isMultipartContent(request)) {

                  // 按照傳統方式獲取數據

                  return;

             }

             // 四、使用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("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);

                         // 獲取item中的上傳文件的輸入流

                         InputStream in = item.getInputStream();

                         // 建立一個文件輸出流

                         FileOutputStream out =

                                    new FileOutputStream(savePath + "\\" + filename);

                         // 建立一個緩衝區

                         byte buffer[] = new byte[1024];

                         // 判斷輸入流中的數據是否已經讀完的標識

                         int len = 0;

                         // 循環將輸入流讀入到緩衝區當中,

                         (len=in.read(buffer))>0就表示in裏面還有數據

                         while ((len = in.read(buffer)) > 0) {

                         out.write(buffer, 0, len);

                         }

                         // 關閉輸入流

                         in.close();

                         // 關閉輸出流

                         out.close();

                         // 刪除處理文件上傳時生成的臨時文件

                         item.delete();

                        message = "文件上傳成功!";

                  }

             }

        } catch (Exception e) {

             message = "文件上傳失敗!";

             e.printStackTrace();

        }

        request.setAttribute("message", message);

        request.getRequestDispatcher("/message.jsp").forward(request, response);

     }

    }

●  在web.xml文件中註冊UploadServlet。

    <?xml version="1.0" encoding="UTF-8"?>

    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns="http://xmlns.jcp.org/xml/ns/javaee"

    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

    version="3.1">

    <servlet>

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

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

    </servlet>

    <servlet-mapping>

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

        <url-pattern>/upload</url-pattern>

    </servlet-mapping>

</web-app>

啓動Tomcat服務器,運行效果如圖2一、圖2二、圖23和圖24所示。

a161af66c7d446988cf2eb7ec9aa8ef8.png

 圖21  上傳文件

 

3b9d0f2020254f32aa65cbdaac1b5b98.png

圖22  上傳文件成功

 

3fdafd5d22ea4d93914aca163ddcc8ba.png

圖23  控制檯中打印了上傳文件的文件名

 

f48c3bce9860452a8cb6e27dc12f705a.png

圖24  服務器端接收到了客戶端上傳的文件

 

文件上傳的細節

上述的代碼雖然能夠成功將文件上傳到服務器上面的指定目錄當中,可是文件上傳功能有許多須要注意的小細節問題,如下列出的幾點須要特別注意的

●  爲保證服務器安全,上傳文件應該放在外界沒法直接訪問的目錄下,好比放於WEB-INF目錄下。

●  爲防止文件覆蓋的現象發生,要爲上傳文件產生一個惟一的文件名。

●  爲防止一個目錄下面出現太多文件,要使用hash算法打散存儲。

●  要限制上傳文件的最大值。

●  要限制上傳文件的類型,在收到上傳文件名時,判斷後綴名是否合法。

●  針對上述提出的5點細節問題,咱們來改進一下UploadServlet,改進後的代碼以下:

package com.xdl.servlet;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

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.ProgressListener;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    public void service(HttpServletRequest request,

         HttpServletResponse response) throws ServletException, IOException {

        // 獲得上傳文件的保存目錄,將上傳的文件存放於WEB-INF目錄下,

        // 不容許外界直接訪問,保證上傳文件的安全

        String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");

        // 上傳時生成的臨時文件保存目錄

        String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");

        File tmpFile = new File(tempPath);

        if (!tmpFile.exists()) {

             // 建立臨時目錄

             tmpFile.mkdir();

        }

        // 消息提示

        String message = "";

        try {

             // 使用Apache文件上傳組件處理文件上傳步驟:

             // 一、建立一個DiskFileItemFactory工廠

             DiskFileItemFactory factory = new DiskFileItemFactory();

             // 設置工廠的緩衝區的大小,當上傳的文件大小超過緩衝區的大小時,

             //就會生成一個臨時文件存放到指定的臨時目錄當中。

             factory.setSizeThreshold(1024 * 100);

             // 設置緩衝區的大小爲100KB,若是不指定,那麼緩衝區的大小默認是10KB

             // 設置上傳時生成的臨時文件的保存目錄

             factory.setRepository(tmpFile);

             // 二、建立一個文件上傳解析器

             ServletFileUpload upload = new ServletFileUpload(factory);

             // 監聽文件上傳進度

             upload.setProgressListener(new ProgressListener() {

                  public void update(long pBytesRead, long pContentLength,

                             int arg2) {

                    System.out.println("文件大小爲:" + pContentLength

                                 + ",當前已處理:" + pBytesRead);

                  }

             });

             // 解決上傳文件名的中文亂碼

             upload.setHeaderEncoding("UTF-8");

             // 三、判斷提交上來的數據是不是上傳表單的數據

             if (!ServletFileUpload.isMultipartContent(request)) {

                  // 按照傳統方式獲取數據

                  return;

             }

             // 設置上傳單個文件的大小的最大值,目前是設置爲1024*1024字節,也就是1MB

             upload.setFileSizeMax(1024 * 1024);

             // 設置上傳文件總量的最大值

             upload.setSizeMax(1024 * 1024 * 10);

             // 四、使用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("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 fileExtName =

                               filename.substring(filename.

                               lastIndexOf(".") + 1);

                      // 若是須要限制上傳的文件類型,那麼能夠經過文件的擴展名來判斷上

                      // 傳的文件類型是否合法

                      System.out.println("上傳的文件的擴展名是:" + fileExtName);

                      // 獲取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;

                       // 循環將輸入流讀入到緩衝區當中

                       while ((len = in.read(buffer)) > 0) {

                         out.write(buffer, 0, len);

                      }

                      // 關閉輸入流

                      in.close();

                      // 關閉輸出流

                      out.close();

                      // 刪除處理文件上傳時生成的臨時文件

                      // item.delete();

                      message = "文件上傳成功!";

                  }

             }

        } 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();

        }

        request.setAttribute("message", message);

        request.getRequestDispatcher("/message.jsp").

                 forward(request, response);

    }

    private String makeFileName(String filename) {

        // 爲防止文件覆蓋的現象發生,要爲上傳文件產生一個惟一的文件名

        return UUID.randomUUID().toString() + "_" + filename;

    }

    /**

     * 爲防止一個目錄下面出現太多文件,要使用hash算法打散存儲

     */

    private String makePath(String filename, String savePath) {

        // 獲得文件名的hashCode的值,獲得的就是filename這個字符串對象在內存中的地址

        int hashcode = filename.hashCode();

        int dir1 = hashcode & 0xf;

        int dir2 = (hashcode & 0xf0) >> 4;

        // 構造新的保存目錄

        String dir = savePath + "\\" + dir1 + "\\" + dir2;

        // File既能夠表明文件也能夠表明目錄

        File file = new File(dir);

        // 若是目錄不存在

        if (!file.exists()) {

             // 建立目錄

             file.mkdirs();

        }

        return dir;

    }

}

針對上述提出的5點小細節問題進行改進以後,咱們的文件上傳功能就算是作得比較完善了。

相關文章
相關標籤/搜索