My Calendar

2015年2月10日 星期二

Servlet 檔案上傳

          要實現檔案上傳之前, 首先要了解 html中enctype這個屬性, enctype 屬性是在表單要發送數據給server之前決定用哪一種編碼。

enctype 有三種屬性:
  • application/x-www-form-urlencoded:默認編碼方式(只是處理表單裡value屬性值)
  • multipart/form-data:會以binary的編碼方式處理數據
  • text/plain :空格轉為"+",但不对特殊字元編碼
既然需要實現檔案上傳, 那我們的enctype當然要用multipart/form-data這個屬性。

index.html
<!DOCTYPE html>
<html>
    <head>
        <title>文件上傳</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <div>
            <form action="upload" method="POST" enctype="multipart/form-data">
                <table>
                    <tr>
                        <td><label for="file1">文件:</label></td>
                        <td><input type="file" id="file" name="file"></td>
                    </tr>
                    <tr>
                        <td colspan="2"><input type="submit" value="上传" name="upload"></td>
                    </tr>
                </table>
            </form>
        </div>
    </body>
</html>

Servlet 2.x 的版本有兩種方法可以獲取上傳的檔案。

1.使用getInputStream()處理binary資料
package servlet.upload.demo;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/upload")
public class UploadServlet extends HttpServlet {

 class Position {

        int begin;
        int end;

  public Position(int begin, int end) {
   this.begin = begin;
   this.end = end;
  }
 }

 private static final long serialVersionUID = -4673819464292623830L;

 public UploadServlet() {
  super();
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  byte[] content = bodyContent(request); // 讀取HTTP Body的內容
  String contentAsTxt = new String(content, "ISO-8859-1"); // 將HTTP Body內容用String表示

  String filename = getFileName(contentAsTxt); // 取得檔案名稱
  Position p = getFilePosition(contentAsTxt, request.getContentType()); // 取得檔案開始和結束位置

  writeTo(filename, content, p);
 }

 private byte[] bodyContent(HttpServletRequest request) throws IOException {

  ByteArrayOutputStream out = new ByteArrayOutputStream();
  InputStream in = request.getInputStream();

  try {
   byte[] buffer = new byte[1024];
   int length = -1;
   while ((length = in.read(buffer)) != -1) {
    out.write(buffer, 0, length);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }

  return out.toByteArray();
 }

 private String getFileName(String requestBody) {
  String fileName = requestBody.substring(requestBody.indexOf("filename=\"") + 10);
  fileName = fileName.substring(0, fileName.indexOf("\n"));
  fileName = fileName.substring(fileName.indexOf("\n") + 1, fileName.indexOf("\""));

  return fileName;
 }

 private Position getFilePosition(String content, String contentType) throws IOException {
  String boundaryText = contentType.substring(contentType.lastIndexOf("=") + 1, contentType.length());
  // 取得實際上傳檔案的起始与结束位置
  int pos = content.indexOf("filename=\"");
  pos = content.indexOf("\n", pos) + 1;
  pos = content.indexOf("\n", pos) + 1;
  pos = content.indexOf("\n", pos) + 1;
  int boundaryLoc = content.indexOf(boundaryText, pos) - 4;
  int begin = ((content.substring(0, pos)).getBytes("ISO-8859-1")).length;
  int end = ((content.substring(0, boundaryLoc)).getBytes("ISO-8859-1")).length;

  return new Position(begin, end);
 }

 private void writeTo(String fileName, byte[] body, Position p) throws IOException {
  FileOutputStream fileOutputStream = new FileOutputStream("d:/" + fileName);
  fileOutputStream.write(body, p.begin, (p.end - p.begin));
  fileOutputStream.flush();
  fileOutputStream.close();
 }

}

實務上表格通常不會只有一個上傳檔案,會同時包含文字輸入,按鈕選項,下拉式選單等等。這時候如果使用request.getParameter()讀取資料,你會發現都拿到null!!!。因為表格的enctype 屬性已經設定成multipart/form-data,資料是以binary傳輸,Servlet 2.x版本的request.getParameter() 預設是不會處理binary的資料,所以得到的值都是null.

如果採用第一種方法那就需要將全部資料從binary截取,那實在太麻煩了。那我們用第三方套件幫組我們完成這件事。

2. 使用第三方套件 Commons File Upload

下載 commons-fileupload.jarcommons-io.jar 然後引入到專案中.
package servlet.upload.demo;

import java.io.File;
import java.util.List;

import javax.servlet.annotation.WebServlet;
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;

@WebServlet("/upload")
public class UploadServlet2 extends HttpServlet {

 private static final long serialVersionUID = 2372213113534302476L;

 public UploadServlet2() {
  super();
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) {
  try {
   //檢查請求是否用multipart/form-data處理
   if (ServletFileUpload.isMultipartContent(request)) {
    DiskFileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload upload = new ServletFileUpload(factory);
    //解析請求
    List formItems = upload.parseRequest(request);
    if (formItems != null && formItems.size() > 0) {
     for (FileItem item : formItems) {
      //檢查內容是檔案還是文字
      if (!item.isFormField() && item.getName() != null && !item.getName().equals("")) {
       String fileName = new File(item.getName()).getName();
       String filePath = "D:" + File.separator + fileName;
       File storeFile = new File(filePath);
       item.write(storeFile);
      } else {
       System.out.println("Key="+item.getFieldName());
       System.out.println("Value="+item.getString());
      }
     }
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

}


3.使用Servlet 3.0 @MultipartConfig
Servlet 3.0 開始HttpServletRequest提供上傳檔案的支持。
package servlet.upload.demo;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@MultipartConfig(location = "d:/")
@WebServlet("/upload")
public class UploadServlet3 extends HttpServlet {

 public UploadServlet3() {
  super();
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  request.setCharacterEncoding("UTF-8"); // 處理中文檔名
  Part part = request.getPart("file");
  part.write(part.getSubmittedFileName());
 }
}


沒有留言:

張貼留言