前后端分離有時候需要實現文件的上傳與下載。現使用React與Springboot做一個簡單的文件上傳Demo
搭建一個最簡單的基於springboot的app:myservice,過程不再贅述
創建一個簡單的controller,用於與前台交流:
package com.example.myservice.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/file") public class FileController { @RequestMapping(value = "/test", method = RequestMethod.GET) public String test() { return "hello, this file controller"; } }
啟動springboot之后,直接在瀏覽器中get一下,看看是否通了
表示運行成功。
搭建一個簡單的基於React的app,現在借助我現有的React-Typescript框架進行試驗
import React from "react"; function MainApp(props: any) { return <div>hello, world!</div> } export default MainApp;
簡單的頁面代碼。
頁面運行成功。
OK,基本的腳手架搭完,現在就看如何實現。
實現文件上傳:
前端考慮使用偽表單進行上傳,上傳文件希望同時上傳一些其他的數據。
理想中的文件結構:
{
param1: any;
param2: any;
...
file: File;
}
根據上述結構,編寫上傳組件雛形
function FileUpload(props: any) { return <div> <input type="file" /> </div> } function MainApp(props: any) { return <FileUpload /> }
頁面上面已經可以顯示一個上傳文件的按鈕,並且有文件上傳動作交互了。但是文件路徑暫存於瀏覽器中,並沒有放到后台。
如何獲取到文件數據呢?有人建議使用id與getElementById。既然這里用了React,為何不嘗試使用ref引用組件呢?
function FileUpload(props: any) { const inputRef = React.useRef<any>(); return <div> <input ref={inputRef} type="file" /> {/** 加一個小button測試引用 */} <button onClick={function () { console.log(inputRef.current) }}>log the ref</button> </div> } function MainApp(props: any) { return <FileUpload /> }
點擊后面的button,查看控制台打印的對象:
inputref成功到了對象。
下一步的問題就是如何獲取文件。這里上傳一個文件,然后針對ref進行打印操作,試着獲取各種值:
可見value存儲了文件路徑,files存儲着文件內容
通過實驗發現,執行inputRef.current.value = "" 可以清空文件列表,也就是直接對element對象的value空字符串賦值可以清空文件
這里驗證一下上面所說的:
import React from "react"; function FileUpload(props: any) { return <div> <input type="file" onChange={function (event: React.ChangeEvent<HTMLInputElement>) { console.log(event.target.value); console.log(event.target.files); }} /> </div> } function MainApp(props: any) { return <FileUpload /> } export default MainApp;
結果正確。下面進行文件數據保存的操作
前台代碼:(使用Axios的異步交互框架進行發送數據)
import Axios from "axios"; import React from "react"; function FileUpload(props: any) { const inputRef = React.useRef<any>(); const [uploadData, setUploadData] = React.useState<any>(); function handleChange(event: React.ChangeEvent<HTMLInputElement>) { // 將文件的第一個保存到uploadData中 // 僅考慮單文件上傳,多文件上傳原理差不多 if (event.target.files) { var newUploadData = new FormData(); newUploadData.append("fileInfo", "some file's info"); newUploadData.append("file", event.target.files[0]); setUploadData(newUploadData); } } function handleUpload() { uploadData && Axios.post("http://localhost:8080/file/upload", uploadData, { headers: { "Content-Type": "multipart/form-data" } }).then(function (response: any) { console.log(response.data); }).catch(function (error: any) { console.log(error.response); }) } function handleClean() { setUploadData(undefined); inputRef.current && (inputRef.current.value = ""); } return <div> <input ref={inputRef} type="file" onChange={handleChange} /> <button onClick={handleUpload}>upload</button> <button onClick={handleClean}>clean</button> </div> } function MainApp(props: any) { return <FileUpload /> } export default MainApp;
后台代碼:
簡單的Dto:
package com.example.myservice.dto; import org.springframework.web.multipart.MultipartFile; import lombok.Data;
// lombok注解,可以省略寫getter, setter方法的步驟 @Data public class FileUploadDto { private String fileInfo; private MultipartFile file; }
Controller:
package com.example.myservice.controller; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.example.myservice.dto.FileUploadDto; @RestController @CrossOrigin("*") @RequestMapping("/file") public class FileController { @RequestMapping(value = "/test", method = RequestMethod.GET) public String test() { return "hello, this file controller"; } @RequestMapping(value = "/upload", method = RequestMethod.POST, produces = "multipart/form-data") public String upload(FileUploadDto dto) { System.out.println("fileInfo: " + dto.getFileInfo()); System.out.println("fileSize: " + dto.getFile().getSize()); return "upload successfully!"; } }
上傳文件運行截圖:
后台log:
項目結束。
由於水平有限,文中難免有錯誤遺漏之處,請大家多多指正批評。