【溫故知新】Java web 開發(三)Form表單與上傳下載文件


  簡介:在一和二的基礎之上,這次來記錄下如何在頁面提交表單數據,以及文件的上傳和下載整個流程,請求也不僅限於GET了,也有POST了。

 

1. 為了方便,在 webapp 下直接新建一個 index.html,內容如下

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>歡迎頁</title>
</head>
<body>
<form action="/upload" method="POST" enctype="multipart/form-data">
站點名: <input type="text" name="name"><br />
網址: <input type="text" name="url" /><br />
作者: <input type="text" name="author" /><br />
上傳文件: <input type="file" name="file" /><br />
上傳文件2: <input type="file" name="file2" /><br />
<input type="submit" value="提交" />
</form>
</body> </html>

form 的 action指定請求路徑,這里是/upload,也可以是 process.jsp這種。

method 這里用的是 POST, 其它 GET 也可以用在這里。

enctype 表示的是提交請求中的Content-Type是 multipart/form-data,適用於文件上傳。這里展示下請求的樣式:

input type="file" 使用的是 文件上傳的組件

input type="submit" 會把有 name 屬性的 input 字段提交給 action 所指示的請求。

 

 

2. 新建 FileUploadServlet 來處理文件上傳

這里文件上傳處理,使用了開源組件 commons-fileupload,maven 依賴如下:

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

 

Servlet主要業務代碼如下(代碼里用到了jdk8的lamda表達式,確實省代碼)

邏輯比較簡單,就是用 ServletFileUpload 來解析 request,獲取到提交的文件信息,由於幾個非文件也一並提交了,所以需要判斷分類處理。

文件的上傳和下載都是要使用流的。

@WebServlet(name = "fileUploadServlet", urlPatterns = {"/upload"})
public class FileUploadServlet extends HttpServlet {

    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String message="";
        try {
            String savePath = request.getServletContext().getRealPath("/WEB-INF/upload");
            String tmpPath = request.getServletContext().getRealPath("/WEB-INF/temp");
            File file = new File(savePath);
            if (!file.exists() && !file.isDirectory()) {
                System.out.println(savePath + "目錄不存在,需要創建");
                file.mkdir();
            }
            File tmpFile = new File(tmpPath);
            if (!tmpFile.exists() && !tmpFile.isDirectory()) {
                System.out.println(tmpPath + "目錄不存在,需要創建");
                tmpFile.mkdir();
            }

            DiskFileItemFactory factory = new DiskFileItemFactory();
            // 緩沖區大小設置
            factory.setSizeThreshold(1024 * 100);
            factory.setRepository(tmpFile);
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setProgressListener((pBytesRead, pContentLength, arg2) -> System.out.println("文件大小為:" + pContentLength + ",當前已處理:" + pBytesRead));
            upload.setHeaderEncoding("UTF-8");
            if (!ServletFileUpload.isMultipartContent(request)) {
                //按照傳統方式獲取數據
                return;
            }
            //設置上傳單個文件的大小的最大值,目前是設置為1024*1024字節,也就是1MB
            upload.setFileSizeMax(1024 * 1024 *10);
            //設置上傳文件總量的最大值,最大值=同時上傳的多個文件的大小的最大值的和,目前設置為10MB
            upload.setSizeMax(1024 * 1024 * 100);

            List<FileItem> list = upload.parseRequest(request);
            for (FileItem item : list) {
                if (item.isFormField()) {
                    String name = item.getFieldName();
                    String value = item.getString("UTF-8");
                    // form 表單提交過的 enctype="multipart/form-data"
                    request.setAttribute(name,value);
                    System.out.println(name + "=" + value);
                } else {
                    String filename = item.getName();
                    System.out.println(filename);
                    if (filename == null || "".equals(filename.trim())) {
                        continue;
                    }
                    filename = filename.substring(filename.lastIndexOf(File.separator) + 1);
                    InputStream in = item.getInputStream();
                    FileOutputStream out = new FileOutputStream(savePath + File.separator + filename);
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = in.read(buffer)) > 0) {
                        out.write(buffer, 0, len);
                    }
                    in.close();
                    out.close();
                    //刪除處理文件上傳時生成的臨時文件
                    item.delete();
                }
            }
        } catch (FileUploadBase.FileSizeLimitExceededException e) {
            message = "單個文件超出最大值!";
            System.out.println(message);
            request.setAttribute("message", message);
            request.getRequestDispatcher("/WEB-INF/page/message.jsp").forward(request, response);
        } catch (FileUploadBase.SizeLimitExceededException e) {
            message = "上傳文件總大小超出最大值!";
            System.out.println(message);
            request.setAttribute("message", message);
            request.getRequestDispatcher("/WEB-INF/page/message.jsp").forward(request, response);
        } catch (FileUploadException e) {
            message = "上傳文件失敗!";
            System.out.println(message);
            request.setAttribute("message", message);
            request.getRequestDispatcher("/WEB-INF/page/message.jsp").forward(request, response);

        }
        request.getRequestDispatcher("/WEB-INF/page/file_upload_result.jsp").forward(request, response);
    }
}

  

3.  file_upload_result.jsp 是展示上傳結果的頁面

這里有個細節需要注意,就是之前傳的幾個字段是用 multipart/form-data 上傳的,那么解析的時候就不能直接用 getParameter了,為了方便起見,我在之前的處理過程中,事先 setAttribute 了一下。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>表單處理展示</title>
</head>
<body>
    <%
        String name = (String)request.getAttribute("name");
        String url = (String)request.getAttribute("url");
        String author = (String)request.getAttribute("author");
    %>
    <li>您輸入的網站是:<%=name%></li>
    <li>網站名是:<%=url%></li>
    <li>作者:<%=author%></li><br />
    <div>點擊這里查看上傳過的文件類別:<a href="/list">這里</a></div>
</body>
</html>

 

4. 上傳文件列表展示

@WebServlet(name = "fileListServlet", urlPatterns = {"/list"})
public class FileListServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String filePath = request.getServletContext().getRealPath("/WEB-INF/upload");
        Map<String, String> map = new HashMap<>(8);
        listFile(new File(filePath), map);
        request.setAttribute("fileMap", map);
        request.getRequestDispatcher("/WEB-INF/page/file_list.jsp").forward(request, response);
    }

    private void listFile(File file, Map<String, String> fileNameMap) {
        if (file.isDirectory()) {
            File[] fileList = file.listFiles();
            for (File innerFile : fileList) {
                listFile(innerFile, fileNameMap);
            }
        } else {
            String fileName = file.getName();
            fileNameMap.put(fileName, fileName);
        }
    }
}

展示的頁面

<%@ page import="java.util.Map" %>
<%@ page import="java.net.URLEncoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>文件展示頁</title>
</head>
<body>
    <%
        Map<String, String> map = (Map<String, String>)request.getAttribute("fileMap");
        if(map == null ||map.size() == 0) {
            out.println("您還沒有上傳文件,請點擊這里上傳:<a href=/index.html>上傳文件</a>");
        }
        for (String str: map.keySet()) {
            out.println("文件名:" + str + "&nbsp;&nbsp;&nbsp; <a href=/download?fileName=" + URLEncoder.encode(map.get(str), "utf-8")+ ">下載</a><br />");
        }
    %>
</body>
</html>

 

5. 文件下載

下載文件有個需要注意的地方就是文件名的亂碼問題。由於 HTTP 請求頭必須是 ISO-8859-1 編碼,傳送的時候一定要改成這個編碼

 

@WebServlet(name = "fileDownloadServlet", urlPatterns = {"/download"})
public class FileDownloadServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fileName = request.getParameter("fileName");
        System.out.println("fileName before ============" + fileName);
        String uploadPath = request.getServletContext().getRealPath("/WEB-INF/upload");
        File file = findFilePath(fileName, new File(uploadPath));
        if (file != null) {
            response.setContentType("application/octet-stream");
            fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
            response.setHeader("content-disposition", "attachment;filename=" + fileName);
            FileInputStream input = new FileInputStream(file);
            OutputStream out = response.getOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = input.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            input.close();
            out.close();
        } else {
            request.setAttribute("message", "您要下載的資源不存在或者已被刪除!");
            request.getRequestDispatcher("/WEB-INF/page/message.jsp").forward(request, response);
        }
    }

    private File findFilePath(String fileName, File file) {
        if (file == null || !file.isDirectory()) {
            return null;
        } else {
            for (File innerFile : file.listFiles()) {
                if (innerFile.isFile()) {
                    if (innerFile.getName().equals(fileName)) {
                        return innerFile;
                    }
                } else {
                    return findFilePath(fileName, innerFile);
                }
            }
        }
        return null;
    }
}

 

 6. 亂碼問題(非全面總結,近記錄下個人遇到的問題)

不是設置了 request.setCharacterEncoding("UTF-8"); 就不會出現亂碼問題,還得看容器的設置,比如說 Tomcat 的話,得看 server.xml 中的兩個配置 useBodyEncodingForURI="true" URIEncoding="UTF-8"

useBodyEncodingForURI參數表示是否用request.setCharacterEncoding參數對URL提交的數據和表單中GET方式提交的數據進行重新編碼,在默認情況下,該參數為false。
 
URIEncoding參數指定對所有GET方式請求進行統一的重新編碼(解碼)的編碼。
 
前者優先級高,如果你前者設置了true,但是代碼里沒有寫request.setCharacterEncoding,那么即便后者有設置也不管用,也就是說一旦設置了前者后者就失效了

<Connector port="8080" protocol="HTTP/1.1"   
        connectionTimeout="20000"   
        redirectPort="8444"   
        useBodyEncodingForURI="true" URIEncoding="UTF-8"/>

  

7. input 與 button

<input type="submit" />
<input type="button" />
<button type="submit" />
<button type="button" /
 
只有 type 為 submit的能夠提交表單,type 為 button 的只能通過 js 或者 ajax 事件來實現發送請求傳遞參數。
那么為什么存在 button 呢?因為它比單純的 input 表現力更強,它的 value 值可以是圖片等,input 只能是文字。
另外,input 里邊必須有 name 才能被表單伴隨提交。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM