iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 17
2
Modern Web

30天學習Spring MVC系列 第 17

Day 17 Spring Boot-檔案上傳篇

  • 分享至 

  • xImage
  •  

前言

這一章我要介紹的是檔案上傳的功能,這篇看似不起眼,有人會認為說網頁檔案上傳不就點下upload file按鈕後再選擇檔案然後按一個上傳按鈕而已,
不!!!!! 你大錯特錯!!!!!
為什麼我要這樣說?
首先要先分四個層面來講
1.上傳檔案有沒有被偽造了?
2.上傳檔案大小定義,上傳檔案的次數需不需要限制,上傳檔案的名稱有跟你的目的地有無重複
3.上傳哪種檔案?
4.上傳的模式,多檔案上傳我是不是要一個一個點按鈕?

先看這四點好了,開發網站的你是否曾經都遇過這些問題了?
上傳檔案雖然沒什麼但是卻往往都是資訊安全最薄弱的一層,但是我們不是要討論資訊安全,所以我們先以功能開發為主
有時間會再來跟大家說明簡單的防護措施,不過最好避免你的檔案上傳功能有使用外掛工具
ex: CKEditor

設定檔配置

pom.xml

如果不想要使用thymeleaf靜態資源載入的方式,你可以上網查詢你要的webjars
將他放入你的pom.xml設定檔
ex:我可能需要jquery,讓Maven來管理我們的webjars

        <dependency>
          <groupId>org.webjars</groupId>
          <artifactId>jquery</artifactId>
          <version>2.2.4</version>
        </dependency>

建立一個頁面,我們需要一個頁面來做上傳按鈕

<form method="POST" enctype="multipart/form-data" id="UploadForm">
     <input type="file" name="files"/><br/>
     <button type="submit" value="Submit" id="btnSubmit"/>
</form>

使用ajax傳輸

<script>

$(function(){
	$("#btnSubmit").click(function (event) {

        //ajax提交的話她會一直重新run程式直到回傳,你可以將此行註解使用開發者工具看一下console就知道了
        event.preventDefault();

        uploadFile();

    });
	
	 uploadFile = function() {

	    // 取得form
	    var form = $('#UploadForm')[0]; //取得HTML中第一個form id為UploadForm

	    var data = new FormData(form); //將form內的所有訊息打包成FormData object

	
	    $("#btnSubmit").prop("disabled", true);

	    $.ajax({
	        type: "POST",               //使用POST傳輸,檔案上傳只能用POST
	        enctype: 'multipart/form-data', //將資料加密傳輸 檔案上傳一定要有的屬性
	        url: "/api/upload/multi", //要傳輸對應的接口
	        data: data,         //要傳輸的資料,我們將form 內upload打包成data
	        processData: false, //防止jquery將data變成query String
	        contentType: false, 
	        cache: false,  //不做快取
	        async : false, //設為同步
	        timeout: 1000000, //設定傳輸的timeout,時間內沒完成則中斷
	        success: function (data) {

	            $("#result").text(data);//填入提示訊息到result標籤內
	            console.log("SUCCESS : ", data);
	            $("#btnSubmit").prop("disabled", false);
	
	        },
	        error: function (e) {

	            $("#result").text(e.responseText); //填入提示訊息到result標籤內
	            console.log("ERROR : ", e);
	            $("#btnSubmit").prop("disabled", false);

	        }
	    })
	}
  })
</script>

建立一個Class:upload Model

我的名稱設為 UploadFileModel.java

package com.tutorial.Model;

import org.springframework.web.multipart.MultipartFile;

public class UploadFileModel {
	 private String extraField;
	/**
	 * 
	 * 單一檔案使用MultipartFile資料型態
	 * 多檔案上傳可使用MultipartFile
	 */
	 private MultipartFile[] files; 

	public String getExtraField() {
		return extraField;
	}

	public void setExtraField(String extraField) {
		this.extraField = extraField;
	}

	public MultipartFile[] getFiles() {
		return files;
	}

	public void setFiles(MultipartFile[] files) {
		this.files = files;
	}
	 
}

建立一個Controller接口

在Class設定一個全域變數為檔案上傳的目的,你可以設定在application.properties

 private static String UPLOADED_FOLDER = ".//upload//";
	// 多個檔案上傳的接口
	    @PostMapping("/api/upload/multi")
        @ResponseBody
	    public ResponseEntity<?> uploadFileMulti(
	            @RequestParam("files") MultipartFile[] uploadfiles) {


	        // 取得檔案名稱
	        String uploadedFileName = Arrays.stream(uploadfiles).map(x -> x.getOriginalFilename())
	                .filter(x -> !StringUtils.isEmpty(x)).collect(Collectors.joining(" , "));

	        if (StringUtils.isEmpty(uploadedFileName)) {
	            return new ResponseEntity("請選擇檔案!", HttpStatus.OK);
	        }

	        try {

	            saveUploadedFiles(Arrays.asList(uploadfiles));

	        } catch (IOException e) {
	            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
	        }

	        return new ResponseEntity("成功上傳 - "
	                + uploadedFileName, HttpStatus.OK);

	    }

檔案上傳存檔的方法

 //將檔案儲存
	    private void saveUploadedFiles(List<MultipartFile> files) throws IOException {

	        for (MultipartFile file : files) {

	            if (file.isEmpty()) {
	                continue; //繼續下一個檔案
	            }

	            byte[] bytes = file.getBytes();
	            Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
	            Files.write(path, bytes);

	        }

	    }

重點介紹

1.此範例為多檔案上傳,如果你想知道更多種檔案上傳的方式你可以看我的參考資料,內有三種檔案上傳方式
2.此範例我是用Controller不是RestController,在接收的接口要加上@ResponseBody
3.@RequestParam() 為接收從POST過來的參數
4.upload Model中的 MultipartFile為檔案陣列型態
5.return後我們會傳送HttpStatus返回時可以處理各種不同的HttpStatus
6.ajax傳輸要很小心的避免使用,如果再ajax函式內使用console你就知道進入了ajax傳輸,javascript還是持續的在等待接收與執行回傳資料

參考資料

(https://www.mkyong.com/spring-boot/spring-boot-file-upload-example-ajax-and-rest/ )
(https://spring.io/guides/gs/uploading-files/ )

假日會介紹建構RESTful Web Service將剩下的會員 CRUD完成
如果你喜歡我的文章,請大家辦個帳號來幫我點個訂閱吧!!
感謝觀看我文章的人 累阿~~


上一篇
Day 16 Spring Boot-Spring Session介紹(下)
下一篇
Day 18 Spring Boot RESTful Web Service (上)
系列文
30天學習Spring MVC30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
Ho.Chun
iT邦新手 5 級 ‧ 2019-01-26 21:28:12

請問您的 UploadFileModel 在哪邊使用到 ??

cfox iT邦新手 3 級 ‧ 2019-07-10 15:56:21 檢舉

UploadFileModel 只有使用 ModelAttribute 時才會用到。
可以看參考資料中的 3.1.3

0
aa4731073
iT邦新手 4 級 ‧ 2022-03-28 23:57:21

請問有postman的測試教學嗎?

我要留言

立即登入留言