網頁

2018/3/19

使用jQuery.ajax()上傳檔案至Spring MVC Controller

本篇介紹如何使用jQuery.ajax()來非同步上傳檔案至Spring MVC的Controller。

傳送檔案的jsp頁面file-upload.jsp

<%@ 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">
<script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
</head>
<body>
<div>
  <form id="myForm">
    <input type="file" name="file" id="file"/>
    <button type="submit">上傳檔案</button>
  </form>
</div>
</body>
<script>
$(function(){
  $("#myForm").submit(function(e){
    e.preventDefault(); // 停止觸發submit
    console.log("upload");
    var formData = new FormData($("#myForm")[0]); // 使用FormData包裝form表單來傳輸資料
    $.ajax({
      type: "POST",
      url: "upload",
      data:formData,
      cache:false, // 不需要cache
      processData: false, // jQuery預設會把data轉為query String, 所以要停用
      contentType: false, // jQuery預設contentType為'application/x-www-form-urlencoded; charset=UTF-8', 且不用自己設定為'multipart/form-data'
      dataType: 'text',
      success: function (data){
        console.log(data);
      }      
    });
  });
});
</script>
</html>

contentType務必要設為false,不要自己設為"multipart/form-data",因為如果自己設定為"multipart/form-data"會缺少boundary參數,Spring MVC在處理時會出現下面錯誤。

org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found

Spring MVC的配置檔設定,因為要上傳檔案記得要設定MultipartResolver。因為這邊使用CommonsMultipartResolver,所以必須將依賴的Apache Commons FileUpload的函式庫加入專案的Libraries

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/mvc 
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context.xsd">
        
  <mvc:annotation-driven /><!-- 啟用Spring MVC annotation配置,例如@Controller -->
  <context:component-scan base-package="idv.matt.controller"/> <!-- 透過掃描@Component來註冊Bean -->
  <!-- MultipartResolver -->
  <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="20971520" /> <!-- 20MB -->
    <property name="maxUploadSizePerFile" value="5242880"/> <!-- 5MB -->
    <property name="maxInMemorySize" value="1048576" /> <!-- 1MB -->
    <property name="defaultEncoding" value="UTF-8" />
  </bean>
  
</beans>

用來接收上傳檔案的MyController,此類別掛有@RestController所以會被配置檔設定的<context:component-scan/>掃描並註冊於Spring容器。

MyController.java

package idv.matt.controller;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

import org.springframework.http.MediaType;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;


@RestController
public class MyController {
  
  @PostMapping(value="upload", produces = MediaType.TEXT_PLAIN_VALUE)
  public String upload(@RequestParam("file") MultipartFile file) throws IOException {
    if (!file.getOriginalFilename().isEmpty()) {
      BufferedOutputStream outputStream = new BufferedOutputStream(
            new FileOutputStream(
                  new File("D:/", file.getOriginalFilename()))); // 上傳檔案位置為D:\
      outputStream.write(file.getBytes());
      outputStream.flush();
      outputStream.close();
    }else{
      return "fail";
    }
    
    return "success";
  }
  
}

注意@RequestParam的值必須等於表單中<input type="file" />name屬性一致,範例中為"file"。如果不一致會出現錯誤HTTP Status 400 Bad Request錯誤如下。

HTTP Status 400 - Required request part 'xxx' is not present

完成以上設定便可以透過Ajax來上傳檔案了。


參考:

沒有留言:

張貼留言