File Upload

File Upload

2021/11/30

Multipart傳送檔案

首先,產生一個上傳的頁面 (src/ui/ImageUpload.js):

import React from 'react';

import {Box, Input} from '@mui/material';

import axios from 'axios';


export default function ImageUpload() {

const handleUpload = function(e){

console.log(e.target.files[0]);

}

return (

<Box>

<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>

</Box>

)


}

應該會在console裡看到類似這樣的內容

File {name: 'cat.jpeg', lastModified: 1576031351526, lastModifiedDate: Wed Dec 11 2019 10:29:11 GMT+0800 (台北標準時間), webkitRelativePath: '', size: 38095, …}

那要怎麼把檔案傳出去呢? 可以使用post。

import React from 'react';

import {Box, Input} from '@mui/material';

import axios from 'axios';


export default function ImageUpload() {

const handleUpload = async function(e){

console.log(e.target.files[0]);

try {

const result = await axios.post("/file",e.target.files[0]);

console.log(result);

}

catch(error){

console.log(error);

}


}

return (

<Box>

<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>

</Box>

)


}

在spring的部分,我們新增對應的rest controller,spring framework提供MultipartFile來接收上傳的檔案。

controller/FileUploadController.java

package com.example.demo.controller;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.multipart.MultipartFile;


@RestController

public class FileUploadController {


@PostMapping("/file")

public String handleFileUpload(@RequestParam("file") MultipartFile file) {

System.out.println(file.getOriginalFilename());

//storageService.store(file);

return "OK";


}

}

但是,會在瀏覽器這邊得到:

Error: Request failed with status code 500

看一下spring這邊,會發現

org.springframework.web.multipart.MultipartException: Current request is not a multipart request

原因是,因為我們要傳遞檔案,不能直接呼叫post,要將資料包在FormData裡。那我們來改一下上傳頁面(src/ui/ImageUpload.js):

import React from 'react';

import {Box, Input} from '@mui/material';

import axios from 'axios';


export default function ImageUpload() {

const handleUpload = async function(e){

console.log(e.target.files[0]);

try {

const formData = new FormData()

formData.append(

'file',e.target.files[0]

)

const result = await axios.post("/file",formData);

console.log(result);

}

catch(error){

console.log(error);

}


}

return (

<Box>

<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>

</Box>

)


}

在console.log可以看到

{data: 'OK', status: 200, statusText: 'OK', headers: {…}, config: {…}, …}

在伺服器端,可以看到上傳檔案的名稱。

Spring儲存檔案

收到檔案之後,接下來要將檔案儲存在伺服器上。習慣上,我們都不直接串連實作的類別,我們先產生一個StorageService,StorageService是個interface,真正的實作是FileSystemStorageService,

service/StorageService.java

package com.example.demo.service;

import org.springframework.web.multipart.MultipartFile;


public interface StorageService {


void store(MultipartFile file);


}

真正的實作,service/FileSystemStorageService.java,在這裡,利用了java內建的Path及Files,將收到的檔案放到指定的路徑(如果路徑是檔案名,就是以該名稱為檔案名)。

package com.example.demo.service;


import org.springframework.stereotype.Service;

import org.springframework.web.multipart.MultipartFile;


import java.io.IOException;

import java.nio.file.Files;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.nio.file.StandardCopyOption;


@Service

public class FileSystemStorageService implements StorageService {


@Override

public void store(MultipartFile file) {

try {

Path p = Paths.get("test.jpg");

Files.copy(file.getInputStream(),p,StandardCopyOption.REPLACE_EXISTING );

} catch (IOException e) {

System.out.println(e);

}

}


}

把服務跟controller串連起來:

package com.example.demo.controller;

import com.example.demo.service.StorageService;


import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.multipart.MultipartFile;


@RestController

public class FileUploadController {


@Autowired

private StorageService storageService;


@PostMapping("/file")

public String handleFileUpload(@RequestParam("file") MultipartFile file) {

System.out.println(file.getOriginalFilename());

storageService.store(file);

return "OK";


}

}

這時候可以看到專案最上面那一層多了一個test.jpg了! 如果我們要把檔案儲存為原本的檔案名稱,我們就改一下,注意,因為我們的設定是REPLACE_EXISTING,所以,如果檔案名稱一樣,會被覆蓋:

Path p = Paths.get(file.getOriginalFilename());

Files.copy(file.getInputStream(),p,StandardCopyOption.REPLACE_EXISTING );

參考資料