在本教程中,你将学习如何使用 Spring Boot 构建文件上传和下载的后端功能,以及使用 React 实现前端界面。示例中还包括文件预览的功能。
目录
- 设置 Spring Boot 后端
- 创建 React 前端
- 整合与测试
设置 Spring Boot 后端
我们首先创建一个简单的 Spring Boot 应用程序来处理文件上传和下载。
-
创建 Spring Boot 项目
使用你喜欢的方法(Spring Initializr、IDE 等)创建一个新的 Spring Boot 项目。确保包含 Spring Web 和 Spring Boot Starter 依赖。
-
实现 FileController 类
在控制器类中实现文件上传和下载的端点。以下是完整的示例代码:
package com.java.springboot.fileupload.controller;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile;import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption;@RestController @RequestMapping("/api") public class FileController {private static final Logger logger = LoggerFactory.getLogger(FileController.class);private final Path uploadDir;public FileController() {// 获取项目根目录String projectRoot = System.getProperty("user.dir");// 构造上传目录的路径,确保路径格式正确uploadDir = Paths.get(projectRoot, "uploads");// 创建 uploads 目录(如果不存在的话)File uploadDirectory = uploadDir.toFile();if (!uploadDirectory.exists()) {if (!uploadDirectory.mkdirs()) {throw new RuntimeException("Failed to create upload directory");}}}@PostMapping("/upload")public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {try {// 获取文件名并清理路径String fileName = StringUtils.cleanPath(file.getOriginalFilename());// 验证文件名if (fileName.contains("..")) {return ResponseEntity.badRequest().body("Invalid file path.");}// 构造文件的保存路径Path path = uploadDir.resolve(fileName);// 保存文件(覆盖已经存在的文件)Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);// 打印日志logger.info("File uploaded successfully: {}", path.toAbsolutePath());return ResponseEntity.ok("File uploaded successfully: " + fileName);} catch (IOException e) {logger.error("Failed to upload file", e); // 打印异常信息到日志return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to upload file");}}@GetMapping("/download/{fileName}")public ResponseEntity<byte[]> downloadFile(@PathVariable String fileName) {try {// 验证文件名if (fileName.contains("..")) {return ResponseEntity.badRequest().body(null);}// 构造文件的路径Path path = uploadDir.resolve(fileName);if (!Files.exists(path)) {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);}// 读取文件内容byte[] data = Files.readAllBytes(path);HttpHeaders headers = new HttpHeaders();headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName);// 尝试获取文件的 MIME 类型String contentType = Files.probeContentType(path);if (contentType == null) {contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE; // 默认内容类型}// 打印日志logger.info("File downloaded successfully: {}", path.toAbsolutePath());return ResponseEntity.ok().headers(headers).contentType(MediaType.valueOf(contentType)).body(data);} catch (IOException e) {logger.error("Failed to download file", e); // 打印异常信息到日志return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);}} }
创建 React 前端
接下来,我们创建一个 React 前端页面,用于文件的上传、下载和预览。
-
创建 FileUploadPage 组件
在
FileUploadPage
组件中实现文件选择、上传、下载以及预览功能。以下是完整的示例代码:import React, { useState } from 'react'; import axios from 'axios';const FileUploadPage = () => {// 文件状态const [file, setFile] = useState(null);// 上传状态const [uploadStatus, setUploadStatus] = useState('');// 下载文件名const [downloadFileName, setDownloadFileName] = useState('');// 下载状态const [downloadStatus, setDownloadStatus] = useState('');// 图像预览 URLconst [previewUrl, setPreviewUrl] = useState('');// 处理文件选择const handleFileChange = (event) => {const selectedFile = event.target.files[0];setFile(selectedFile);// 创建图像预览 URLif (selectedFile) {const objectUrl = URL.createObjectURL(selectedFile);setPreviewUrl(objectUrl);}};// 上传文件处理函数const handleUpload = async () => {if (!file) {setUploadStatus('请选择要上传的文件。');return;}const formData = new FormData();formData.append('file', file);try {// 向服务器发送上传请求const response = await axios.post('http://localhost:7000/api/upload', formData, {headers: {'Content-Type': 'multipart/form-data',},});setUploadStatus(response.data);} catch (error) {setUploadStatus('文件上传失败。');}};// 下载文件处理函数const handleDownload = async () => {if (!downloadFileName) {setDownloadStatus('请输入文件名。');return;}try {// 向服务器发送下载请求const response = await axios.get(`http://localhost:7000/api/download/${downloadFileName}`, {responseType: 'blob', // 以二进制流的形式接收文件});// 创建一个链接元素,设置其 href 为 blob URL,并点击它下载文件const url = window.URL.createObjectURL(new Blob([response.data]));const link = document.createElement('a');link.href = url;link.setAttribute('download', downloadFileName);document.body.appendChild(link);link.click();link.remove();setDownloadStatus('文件下载成功。');} catch (error) {setDownloadStatus('文件下载失败。');}};return (<div><h1>文件上传与下载</h1><div><h2>上传文件</h2><input type="file" onChange={handleFileChange} /><button onClick={handleUpload}>上传</button><p>{uploadStatus}</p>{/* 图像预览区域 */}{previewUrl && (<div><h3>图像预览</h3><img src={previewUrl} alt="Preview" style={{ maxWidth: '500px', maxHeight: '500px' }} /></div>)}</div><div><h2>下载文件</h2><inputtype="text"placeholder="请输入文件名"value={downloadFileName}onChange={(e) => setDownloadFileName(e.target.value)}/><button onClick={handleDownload}>下载</button><p>{downloadStatus}</p></div></div>); };export default FileUploadPage;
-
创建 App 组件
在
App.js
中引入并使用FileUploadPage
组件:import React from 'react'; import FileUploadPage from './FileUploadPage';function App() {return (<div className="App"><FileUploadPage /></div>); }export default App;
整合与测试
-
启动 Spring Boot 应用
运行你的 Spring Boot 应用,确保它在端口
7000
上运行(你可以在application.properties
中配置端口)。 -
启动 React 应用
运行你的 React 应用,确保它可以访问到你的 Spring Boot 后端(通过
http://localhost:7000
)。 -
测试功能
- 打开 React 应用,选择一个文件进行上传,点击上传按钮,并检查
通过本教程,你成功地实现了一个基于 Spring Boot 的文件上传与下载功能,并结合 React 前端展示了如何进行文件选择、上传、下载及预览。这种前后端分离的实现方式不仅提升了用户体验,还使得文件管理更加高效。