在本教程中,你将学习如何使用 Spring Boot 构建文件上传和下载的后端功能,以及使用 React 实现前端界面。示例中还包括文件预览的功能。
我们首先创建一个简单的 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 前端页面,用于文件的上传、下载和预览。
创建 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 (
文件上传与下载
上传文件
{uploadStatus}
{/* 图像预览区域 */}
{previewUrl && (
图像预览
)}
下载文件
setDownloadFileName(e.target.value)}
/>
{downloadStatus}
);
};
export default FileUploadPage;
创建 App 组件
在 App.js
中引入并使用 FileUploadPage
组件:
import React from 'react';
import FileUploadPage from './FileUploadPage';
function App() {
return (
);
}
export default App;
启动 Spring Boot 应用
运行你的 Spring Boot 应用,确保它在端口 7000
上运行(你可以在 application.properties
中配置端口)。
启动 React 应用
运行你的 React 应用,确保它可以访问到你的 Spring Boot 后端(通过 http://localhost:7000
)。
测试功能
通过本教程,你成功地实现了一个基于 Spring Boot 的文件上传与下载功能,并结合 React 前端展示了如何进行文件选择、上传、下载及预览。这种前后端分离的实现方式不仅提升了用户体验,还使得文件管理更加高效。