1. 程式人生 > >JavaWeb 13 檔案的上傳和下載

JavaWeb 13 檔案的上傳和下載

1、檔案的上傳

概念

將本地檔案上傳到遠端伺服器上,檔案是以一個流的形式上傳到伺服器中,這就需要用表單提交

對錶單的要求

⑴ 表單的請求方式必須是POST
⑵ 表單的enctype屬性必須是multipart/form-data
    ① 當該屬性值為application/x-www-form-urlencoded時,在提交表單之前對錶單中的請求引數進行URL編碼。這是預設值
    ② 當該屬性值為multipart/form-data,在使用包含檔案上傳控制元件的表單時,必須使用該值。表單中的表單項會以一個多部件的形式傳送到伺服器,一個表單項就是一個多部件

⑶ 表單項input中的type屬性是file

2、Fileupload

Fileupload是Apache提供的專門用於進行檔案上傳和下載的工具

DiskFileItemFactory類

檔案專案工廠類

構造方法

public DiskFileItemFactory() {}
建立一個工廠類的例項

ServletFileUpload類

檔案解析器

構造方法

public ServletFileUpload(FileItemFactory fileItemFactory) {}
使用提供的工廠建立一個解析器例項

parseRequest

public List parseRequest(HttpServletRequest request) {}
處理從表單提交過來的multipart/form-data流。傳入request物件,得到表單提交過來的所有表單項

setFileSizeMax

public void setFileSizeMax(long fileSizeMax) {}
限制上傳的單個檔案的大小

setSizeMax

public void setSizeMax(long sizeMax) {}
限制上傳的多個檔案的大小

FileItem介面

解析請求之後,每個表單項(多部件)就是一個FileItem物件

isFormField

boolean isFormField();
是否是一個文字輸入框(type=”text”)。如果是檔案框(type=”file”),返回false

getFieldName

String getFieldName();
獲取表單項的name屬性值

getString

String getString(String encoding) throws UnsupportedEncodingException;
以指定的編碼解析文字輸入框(type=”text”)的value屬性值。一般傳入”UTF-8”,以防中文亂碼

getName

String getName();
獲取檔案框(type=”file”)上傳的檔案的名字

注意:針對IE5、IE6,呼叫該方法獲取到的是上傳檔案的絕對路徑

處理方式:
String fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
這樣不管有沒有檔案分隔符(/ 或 \),都可以獲取檔名

getSize

long getSize();
獲取檔案框(type=”file”)上傳的檔案的大小

getContentType

String getContentType();
獲取檔案框(type=”file”)上傳的檔案的MIME型別

write

void write(File file) throws Exception;
將指定檔案寫到伺服器的硬碟上。傳入指定伺服器路徑的檔案物件

上傳示例

【index.jsp】

<h1>檔案的上傳</h1>
<h2><font color="red">${requestScope.errorMsg}</font></h2>
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
    使用者名稱:<input type="text" name="username" /><br /><br />
    檔案1:<input type="file" name="file1" /><br /><br />
    檔案2:<input type="file" name="file2" /><br /><br />
    檔案3:<input type="file" name="file3" /><br /><br />
    <input type="submit" value="上傳" />
</form>

【success.jsp】

<h1>檔案上傳成功!</h1>
<a href="${pageContext.request.contextPath}/index.jsp">返回上傳頁面</a>

【FileUploadServlet】

import java.io.File;
import java.io.IOException;
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.FileSizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class FileUploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

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

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 建立工廠例項
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 建立解析器例項
        ServletFileUpload fileUpload = new ServletFileUpload(factory);

        // 設定單個檔案大小5Kb【單位位元組】
        fileUpload.setFileSizeMax(5 * 1024);
        // 設定多個檔案大小2Mb【單位位元組】
        fileUpload.setSizeMax(2 * 1024 * 1024);

        try {
            // 解析請求獲取每一個表單項
            List<FileItem> fileItems = fileUpload.parseRequest(request);

            int size = fileItems.size();
            for (int i = 0; i < size; i++) {
                FileItem fileItem = fileItems.get(i);

                // 獲取表單項的name屬性值
                String fieldName = fileItem.getFieldName();

                // 是否是一個文字輸入框
                boolean formField = fileItem.isFormField();
                if (formField) {
                    // 獲取表單value屬性值,以UTF-8來解碼
                    String value = fileItem.getString("UTF-8");

                    // 沒有輸入內容
                    if ("".equals(value)) {
                        request.setAttribute("errorMsg", "請輸入內容!");
                        request.getRequestDispatcher("/index.jsp").forward(request, response);
                        return;
                    }

                    System.out.println("表單項name屬性:" + fieldName);
                    System.out.println("使用者輸入的內容:" + value);
                } else {
                    System.out.println("表單name屬性值:" + fieldName);

                    // 獲取檔名
                    String fileName = fileItem.getName();
                    // 獲取檔案大小(單位位元組)
                    long fileSize = fileItem.getSize();
                    // 獲取檔案的MIME型別
                    String fileType = fileItem.getContentType();

                    // 沒有上傳檔案
                    if (0 == fileSize) {
                        request.setAttribute("errorMsg", "請上傳檔案!");
                        request.getRequestDispatcher("/index.jsp").forward(request, response);
                        return;
                    }

                    System.out.println("檔名:" + fileName);
                    System.out.println("檔案大小:" + fileSize + "位元組");
                    System.out.println("檔案MIME型別:" + fileType);

                    String uploadPath = "/upload";
                    // 上傳到伺服器的路徑的絕對路徑
                    uploadPath = getServletContext().getRealPath(uploadPath);
                    File uploadFile = new File(uploadPath);
                    if (!uploadFile.exists()) {
                        uploadFile.mkdirs();
                    }

                    // 相容IE5、IE6獲取到的檔名是絕對路徑
                    fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);

                    // 生成UUID
                    String uuid = UUID.randomUUID().toString().replace("-", "");
                    // 唯一的檔名
                    uploadPath = uploadPath + File.separator + uuid + "_" + fileName;

                    uploadFile = new File(uploadPath);

                    // 寫到伺服器硬碟上
                    fileItem.write(uploadFile);

                }

            }

            // 成功,跳轉到成功頁面
            response.sendRedirect(request.getContextPath() + "/success.jsp");

        } catch (FileSizeLimitExceededException e) {
            request.setAttribute("errorMsg", "單個檔案最大5Kb");
            request.getRequestDispatcher("/index.jsp").forward(request, response);

            System.out.println("單個檔案錯誤資訊:" + e.getMessage());
        } catch (SizeLimitExceededException e) {
            request.setAttribute("errorMsg", "多個檔案最大2Mb");
            request.getRequestDispatcher("/index.jsp").forward(request, response);

            System.out.println("多個檔案錯誤資訊:" + e.getMessage());
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

3、檔案上傳的注意事項

⑴ 匯入正確的包
    org.apache.commons.fileupload

不是
    org.apache.tomcat.util.http

⑵ 使用者上傳超過指定的單個檔案大小,會報異常:

FileSizeLimitExceededException

超過多個檔案大小,會報異常:

SizeLimitExceededException
在catch這些異常時,有時會給使用者以響應,通過request域設定錯誤資訊,但是當用戶上傳的檔案超過限制的大小過大時,並不會轉發,這是tomcat的bug

4、檔案的下載

概念

將檔案從遠端伺服器下載到本地

檔案下載,只需將要下載的資源的地址放到超連結中,點選即可下載
但如果瀏覽器支援要下載的格式,將會直接開啟,而且通過超連結下載,不能設定許可權,所以一般將要下載的資源放到WEB-INF目錄下,通過Servlet發請求來下載

具體步驟

⑴ 建立一個輸入流,指向WEB-INF下的資原始檔
Tips:可以通過ServletContext的getRealPath方法獲取檔案的真實路徑

⑵ 設定響應頭資訊
    ① 設定檔案的MIME型別
public void setContentType(String type);
傳入檔案的MIME型別

各種檔案的MIME型別,可以從tomcat伺服器的web.xml中搜索Default MIME Type Mappings檢視

一些MIME:

    doc           application/msword
    jpg或jpeg     image/jpeg
    json          application/json
    mp3           audio/mpeg

    Tips:通過呼叫ServletContext的getMimeType方法獲取檔案的MIME型別
public String getMimeType(String file);
傳入檔案的絕對路徑,可以獲取檔案的MIME型別

也可以告訴瀏覽器,這是一個檔案流
response.setContentType(“application/octet-stream;charset=UTF-8”);

    ② 告訴瀏覽器如何處理下載的資源
response.setHeader(“Content-Disposition”, “attachment; filename=”+fileName);
告訴瀏覽器以附件的形式處理下載的資源
如果不設定,瀏覽器如果支援該資源格式,可能會自己開啟資原始檔

    Tips:需要先宣告給瀏覽器響應時顯示的檔案的名字(fileName)

⑶ 建立一個輸出流,將資源輸出到瀏覽器
public ServletOutputStream getOutputStream() throws IOException;
獲取輸出流,用於給瀏覽器輸出二進位制資料。通過response物件來呼叫

⑷ 編寫檔案複製的程式碼
    可以宣告byte型別的 陣列(緩衝),int型別的 獲取到的資料的長度,通過while迴圈來複制檔案

也可以通過
    commons-io-2.5.jar包裡的IOUtils類的copy方法
public static int copy(final InputStream input, final OutputStream output) throws IOException {}
傳入InputStream和OutputStream,即可以複製檔案

對中文檔名的處理

對於Chrome和IE瀏覽器

呼叫URLEncoder的encode方法
public static String encode(String s, String enc) {}
使用指定的編碼機制將字串轉換為 application/x-www-form-urlencoded 格式

    URLEncoder.encode(fileName, “UTF-8”);
    這是固定寫法,傳入中文檔名,即可對其進行URL編碼

對於火狐瀏覽器

對於火狐瀏覽器需要對中文檔名進行BASE64編碼
需要呼叫sun.misc.BASE64Encoder的encode方法

    Tips:可以通過request.getHeader(“User-Agent”);,獲取瀏覽器資訊,再判斷其中是否包含”Mozilla”和”Firefox”,即可判斷是否是火狐瀏覽器

String fileName = "=?utf-8?b?"+new BASE64Encoder().encode(fileName.getBytes())+"?=";

    這是固定寫法。需要事先宣告中文檔名

通用方法

fileName = new String(fileName.getBytes(), “ISO-8859-1”);

注意事項

    宣告的檔名需要帶副檔名,否則下載的時候,會以沒有副檔名的檔案的形式下載

下載示例

【jsp頁面的body內容】

<a href="${pageContext.request.contextPath}/download">下載資源!</a>

【FileDownloadServlet】

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

import sun.misc.BASE64Encoder;

public class FileDownloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

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

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String filePath = "/WEB-INF/files/???.???";

        ServletContext servletContext = getServletContext();
        // 獲取檔案在伺服器上的真實路徑
        filePath = servletContext.getRealPath(filePath);

        // 輸入流指向資原始檔
        InputStream input = new FileInputStream(filePath);

        // 獲取資原始檔的MIME型別
        String mimeType = servletContext.getMimeType(filePath);

        // 設定響應資原始檔的型別
        response.setContentType(mimeType);

        // 給瀏覽器響應時,顯示的檔名
        String fileName = "???.???";

        // 獲取瀏覽器資訊
        String UA = request.getHeader("User-Agent");
        // 判斷是否是火狐瀏覽器
        if (UA.contains("Mozilla") && UA.contains("Firefox")) {
            // 如果是火狐瀏覽器,需要對中文檔名進行BASE64編碼
            fileName = "=?utf-8?b?" + new BASE64Encoder().encode(fileName.getBytes()) + "?=";
        } else {
            // 其他瀏覽器可以使用URL編碼
            fileName = URLEncoder.encode(fileName, "UTF-8");
        }

        // 告訴瀏覽器以附件形式處理響應的資原始檔
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

        // 獲取輸出流
        ServletOutputStream output = response.getOutputStream();

        // 複製檔案
        IOUtils.copy(input, output);

        // 關閉流
        output.close();
        input.close();
    }

}