Spring Boot 和 React 文件上传与下载教程

在本教程中,你将学习如何使用 Spring Boot 构建文件上传和下载的后端功能,以及使用 React 实现前端界面。示例中还包括文件预览的功能。

目录
  1. 设置 Spring Boot 后端
  2. 创建 React 前端
  3. 整合与测试

设置 Spring Boot 后端

我们首先创建一个简单的 Spring Boot 应用程序来处理文件上传和下载。

  1. 创建 Spring Boot 项目

    使用你喜欢的方法(Spring Initializr、IDE 等)创建一个新的 Spring Boot 项目。确保包含 Spring Web 和 Spring Boot Starter 依赖。

  2. 实现 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 前端页面,用于文件的上传、下载和预览。

  1. 创建 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;
    
  2. 创建 App 组件

    App.js 中引入并使用 FileUploadPage 组件:

    import React from 'react';
    import FileUploadPage from './FileUploadPage';
    
    function App() {
        return (
            <div className="App">
                <FileUploadPage />
            </div>
        );
    }
    
    export default App;
    

整合与测试

  1. 启动 Spring Boot 应用

    运行你的 Spring Boot 应用,确保它在端口 7000 上运行(你可以在 application.properties 中配置端口)。

  2. 启动 React 应用

    运行你的 React 应用,确保它可以访问到你的 Spring Boot 后端(通过 http://localhost:7000)。

  3. 测试功能

    • 打开 React 应用,选择一个文件进行上传,点击上传按钮,并检查

通过本教程,你成功地实现了一个基于 Spring Boot 的文件上传与下载功能,并结合 React 前端展示了如何进行文件选择、上传、下载及预览。这种前后端分离的实现方式不仅提升了用户体验,还使得文件管理更加高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DZSpace

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值