在本教程中,你将学习如何使用 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(''); // 图像预览 URL const [previewUrl, setPreviewUrl] = useState(''); // 处理文件选择 const handleFileChange = (event) => { const selectedFile = event.target.files[0]; setFile(selectedFile); // 创建图像预览 URL if (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> <input type="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 前端展示了如何进行文件选择、上传、下载及预览。这种前后端分离的实现方式不仅提升了用户体验,还使得文件管理更加高效。