Spring Boot入门(8)文件上传和数据预览

简介: 介绍  本次分享将具体介绍如何在Spring Boot中实现文件上传和数据预览。   我们将实现CSV文件的数据预览和部分图片格式的查看。

介绍

  本次分享将具体介绍如何在Spring Boot中实现文件上传和数据预览。
  我们将实现CSV文件的数据预览和部分图片格式的查看。主要的思路如下:

  • 利用Spring Boot的MultipartFile实现文件的上传;
  • 利用Spring MVC实现整个文件上传流程的控制;
  • 利用javacsv API实现CSV文件的读取;
  • 利用JavaScript实现CSV文件的数据预览

  话不多说,我们直接上项目!

项目介绍

  整个项目的结构如下:


整个项目的结构

  主要是两部分的内容,一部分是Controller(控制器),一部分是view(视图)文件。UploadController.java是控制器,其代码如下:

package com.hello.upload.Controller;

import java.util.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
public class UploadController{

    private String filename;
    //Save the uploaded file to this folder
    private static String UPLOADED_FOLDER = "E://upload/src/main/resources/static/images/";

    /*
    **文件上传页面
     */

    @GetMapping("/upload")
    public String upload(){
        return "upload";
    }

    @PostMapping("/upload")
    public String singleFileUpload(@RequestParam("file") MultipartFile file,
                                   RedirectAttributes redirectAttributes) {

        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("message", "文件为空! 请选择非空文件上传!");
            return "redirect:/uploadStatus";
        }

        try {
            // 获取文件并保存到指定文件夹中
            byte[] bytes = file.getBytes();
            filename = file.getOriginalFilename();
            Path path = Paths.get(UPLOADED_FOLDER + filename);
            Files.write(path, bytes);

            redirectAttributes.addFlashAttribute("message", "您已成功上传 '" + filename + "', 该文件大小约为 " +bytes.length/1024+" KB.");

        }
        catch (IOException e) {
            e.printStackTrace();
        }

        return "redirect:/uploadStatus";
    }

    /*
    ** 文件上传信息处理页面
     */
    @GetMapping("/uploadStatus")
    public String uploadStatus(){
        return "uploadStatus";
    }

    /*
    ** 数据预览页面: 支持CSV文件和部分图片格式的预览
     */

    @GetMapping("/review")
    public String review(Map<String, Object> map) {

        map.put("filename", filename);
        String filetype = filename.split("\\.")[1];
        map.put("filetype",filetype);
        System.out.println(filename);

        if(filetype.equals("csv")) {
            readCSV read_csv = new readCSV(UPLOADED_FOLDER + filename);
            List<String> result = read_csv.read();
            map.put("result", result);
        }

        return "review";
    }

}

  文件上传的错误处理包含在代码GlobalExceptionHandler.java中,其代码如下:

package com.hello.upload.Controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MultipartException.class)
    public String handleError1(MultipartException e, RedirectAttributes redirectAttributes) {

        redirectAttributes.addFlashAttribute("message", e.getCause().getMessage());
        return "redirect:/uploadStatus";

    }

}

  接着readCSV.java主要实现CSV文件内容的读取,其代码如下:

package com.hello.upload.Controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.csvreader.CsvReader;

/*
** readCSV类:实现CSV文件内容的读取
 */
public class readCSV {

    // 参数:文件路径
    private String file_path;

    // 构造函数
    readCSV(){}

    readCSV(String file_path){
        this.file_path = file_path;
    }

    // getter and setter
    public String getFile_path() {
        return file_path;
    }

    public void setFile_path(String file_path) {
        this.file_path = file_path;
    }

    // read()函数实现具体的读取CSV文件内容的方法
    public List<String> read() {

        List<String> result = new ArrayList<>();

        try {
            // 创建CSV读对象
            CsvReader csvReader = new CsvReader(file_path);
            while (csvReader.readRecord()){
                // 读取每一行数据,以逗号分开
                // System.out.println(csvReader.getRawRecord());
                result.add(csvReader.getRawRecord());
            }
            csvReader.close();
            return result;

        } catch (IOException e) {
            e.printStackTrace();
            return result;
        }
    }

}

  接着是视图部分,文件都位于templates文件夹下。upload.html文件主要是文件上传的页面,其代码如下:

<!DOCTYPE HTML>

<html xmlns:th="http://www.thymeleaf.org">

<head>
    <title>Upload files by Spring Boot</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>

<center>
    <br><br><br>
    <div class="panel panel-primary" style="width:600px">
        <div class="panel-heading">
            <h3 class="panel-title">文件上传</h3>
        </div>
        <div class="panel-body">
            <form class="form-horizontal" role="form" method="POST" action="/upload" enctype="multipart/form-data">

                <div class="form-group" style="width:600px">
                    <label for="upload" class="col-sm-2 control-label">选择文件</label>
                    <div class="col-sm-10">
                        <input type="file" id="upload" name="file" />
                    </div>
                </div>


                <div class="form-group">
                    <div>
                        <button type="submit" class="btn btn-success" id="btn_upload">提交</button>
                        <input type="reset" class="btn btn-danger" value="重置" />

                    </div>
                </div>

            </form>
        </div>
    </div>

</center>

</body>

</html>

  uploadStatus.html文件主要用来展示文件上传的信息,包括上传成功和失败的信息,其代码如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<center>
    <br><br>
    <h2>文件上传状态</h2>

    <br><br>
    <div class="panel panel-success" style="width:1000px">

        <div class="panel-heading">
            <h3 class="panel-title">文件上传</h3>
        </div>

        <div class="panel-body">

            <div th:if="${message}">
                <h3 th:text="${message}"/>
            </div>

        </div>

    </div>

    <a href="/upload"><button class="btn btn-primary">返回文件上传</button></a>
    <a href="/review"><button class="btn btn-warning" id="review">数据预览</button></a>
    </center>

</body>

</html>

  接着是数据预览部分的HTML,主要实现CSV文件的预览和部分图片格式的查看,其代码如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>文件预览</title>
    <link href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <link href="/css/table.css" rel="stylesheet">

    <script th:inline="javascript">
    var filetype = [[${filetype}]];
    var filename = [[${filename}]];

    function show(){
        if(filetype == "csv"){
            showTable();
        }
        else if((filetype=="jpg") || (filetype=="png") || (filetype=="gif") || (filetype=="bmp")){
            document.getElementById("content").innerHTML = filename;
            document.getElementById("image").src = "/images/"+filename ;
        }
        else{
            document.getElementById("content").innerHTML = "文件预览只支持csv文件以及部分格式的图片文件!"
        }
    }

    function showTable(){

    document.getElementById("content").innerHTML = filename;
    var thread_content = document.getElementById("index").innerHTML+"";
    if (thread_content == ""){
        var result = /*[[${result}]]*/ null ;
        var col_count = result[0].split(",").length;
        if(result.length > 51){
            var record_count = 51;
        }
        else{
            var record_count = result.length;
        }
        // 展示表格各个变量的名称
        var headers = result[0].split(",");
        row=document.createElement("tr"); //创建行
        for(var i=0; i< col_count; i++){
            th1=document.createElement("th"); //创建单元格
            th1.appendChild(document.createTextNode(headers[i])); //为单元格添加内容
            row.appendChild(th1); //将单元格添加到行内
        }
        document.getElementById("index").append(row); //将行添加到<thread>中
        for(var i=1; i < record_count; i++){
            row=document.createElement("tr"); //创建行
            for(var j=0; j< col_count; j++){
                td1=document.createElement("td"); //创建单元格
                td1.appendChild(document.createTextNode(result[i].split(",")[j])); //为单元格添加内容
                row.appendChild(td1); //将单元格添加到行内
            }
            document.getElementById("body").append(row); //将行添加到<thread>中
        }
        }

    }
</script>
</head>

<body>

<br><br>
<center>

    <button class="btn btn-danger" onclick="show()">文件预览</button>
    <a href="/upload"><button class="btn btn-warning" id="review">返回主界面</button></a>
    <br><br>

    <p id="content"></p>

    <div style="width:800px">
        <table class="table table-striped table-condensed table-responsive">
            <thead id="index"></thead>
            <tbody id="body"></tbody>
        </table>
    </div>

    <img id="image" src="" />

</center>

</body>

</html>

  最后将以下我们的配置文件,application.properties代码如下:

server.port=8100

spring.servlet.multipart.max-file-size=5MB
spring.servlet.multipart.max-request-size=5MB
spring.http.multipart.enabled=false

其设置网络端口为8100,每次上传的文件大小不超过5MB.项目构建文件build.gradle的代码如下:

buildscript {
    ext {
        springBootVersion = '2.0.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile group: 'net.sourceforge.javacsv', name: 'javacsv', version: '2.0'
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

运行及测试

  好不容易写完了项目,不测试一把,对不起观众啊。
  启动Spring Boot项目,在浏览器中输入:http://localhost:8100/upload,页面如下:


上传文件页面

  选择本地的mpg.csv文件上传,它将会上传至E://upload/src/main/resources/static/images/ 文件夹中,上传成功后的页面如下图:


文件上传成功

  点击数据预览按钮,跳转到review.html页面,再点击该页面的数据预览按钮,显示的页面如下图:


CSV文件预览

  当然也支持部分图片格式的预览,例如:movie.jpg,如下图:


图片预览

  当上传文件大于5MB时,显示的信息如下:


错误信息展示

  本次分享到此结束,该项目的Github地址为: https://github.com/percent4/Upload-File-Using-Spring-Boot 。 接下来还会继续更新Spring Boot方面的内容,欢迎大家交流~~

注意:本人现已开通两个微信公众号: 用Python做数学(微信号为:python_math)以及轻松学会Python爬虫(微信号为:easy_web_scrape), 欢迎大家关注哦~~

目录
相关文章
|
2月前
|
人工智能 Java API
Spring AI 实战|Spring AI入门之DeepSeek调用
本文介绍了Spring AI框架如何帮助Java开发者轻松集成和使用大模型API。文章从Spring AI的初探开始,探讨了其核心能力及应用场景,包括手动与自动发起请求、流式响应实现打字机效果,以及兼容不同AI服务(如DeepSeek、通义千问)的方法。同时,还详细讲解了如何在生产环境中添加监控以优化性能和成本管理。通过Spring AI,开发者可以简化大模型调用流程,降低复杂度,为企业智能应用开发提供强大支持。最后,文章展望了Spring AI在未来AI时代的重要作用,鼓励开发者积极拥抱这一技术变革。
793 71
Spring AI 实战|Spring AI入门之DeepSeek调用
|
3月前
|
安全 Java 数据库
Spring Security 实战指南:从入门到精通
本文详细介绍了Spring Security在Java Web项目中的应用,涵盖登录、权限控制与安全防护等功能。通过Filter Chain过滤器链实现请求拦截与认证授权,核心组件包括AuthenticationProvider和UserDetailsService,负责用户信息加载与密码验证。文章还解析了项目结构,如SecurityConfig配置类、User实体类及自定义登录逻辑,并探讨了Method-Level Security、CSRF防护、Remember-Me等进阶功能。最后总结了Spring Security的核心机制与常见配置,帮助开发者构建健壮的安全系统。
206 0
|
4月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult&lt;T&gt;`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码&quot;0&quot;和消息&quot;操作成功!&quot;,有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
233 0
|
4月前
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——使用 fastJson 处理 null
本文介绍如何使用 fastJson 处理 null 值。与 Jackson 不同,fastJson 需要通过继承 `WebMvcConfigurationSupport` 类并覆盖 `configureMessageConverters` 方法来配置 null 值的处理方式。例如,可将 String 类型的 null 转为 &quot;&quot;,Number 类型的 null 转为 0,避免循环引用等。代码示例展示了具体实现步骤,包括引入相关依赖、设置序列化特性及解决中文乱码问题。
123 0
|
4月前
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——Spring Boot 默认对Json的处理
本文介绍了在Spring Boot中返回Json数据的方法及数据封装技巧。通过使用`@RestController`注解,可以轻松实现接口返回Json格式的数据,默认使用的Json解析框架是Jackson。文章详细讲解了如何处理不同数据类型(如类对象、List、Map)的Json转换,并提供了自定义配置以应对null值问题。此外,还对比了Jackson与阿里巴巴FastJson的特点,以及如何在项目中引入和配置FastJson,解决null值转换和中文乱码等问题。
347 0
|
1月前
|
Java 关系型数据库 MySQL
【Spring】【事务】初学者直呼学会了的Spring事务入门
本文深入解析了Spring事务的核心概念与使用方法。Spring事务是一种数据库事务管理机制,通过确保操作的原子性、一致性、隔离性和持久性(ACID),维护数据完整性。文章详细讲解了声明式事务(@Transactional注解)和编程式事务(TransactionTemplate、PlatformTransactionManager)的区别与用法,并探讨了事务传播行为(如REQUIRED、REQUIRES_NEW等)及隔离级别(如READ_COMMITTED、REPEATABLE_READ)。
114 1
|
25天前
|
Java API 微服务
Java 21 与 Spring Boot 3.2 微服务开发从入门到精通实操指南
《Java 21与Spring Boot 3.2微服务开发实践》摘要: 本文基于Java 21和Spring Boot 3.2最新特性,通过完整代码示例展示了微服务开发全流程。主要内容包括:1) 使用Spring Initializr初始化项目,集成Web、JPA、H2等组件;2) 配置虚拟线程支持高并发;3) 采用记录类优化DTO设计;4) 实现JPA Repository与Stream API数据访问;5) 服务层整合虚拟线程异步处理和结构化并发;6) 构建RESTful API并使用Springdoc生成文档。文中特别演示了虚拟线程配置(@Async)和StructuredTaskSco
110 0
|
3月前
|
人工智能 前端开发 Java
十几行代码实现 Manus,Spring AI Alibaba Graph 快速预览
Spring AI Alibaba Graph 的核心开发已完成,即将发布正式版本。开发者可基于此轻松构建工作流、智能体及多智能体系统,功能丰富且灵活。文章通过三个示例展示了其应用:1) 客户评价处理系统,实现两级问题分类与自动处理;2) 基于 ReAct Agent 的天气预报查询系统,循环执行用户指令直至完成;3) 基于 Supervisor 多智能体的 OpenManus 实现,简化了流程控制逻辑并优化了工具覆盖度。此外,还提供了运行示例的方法及未来规划,欢迎开发者参与贡献。
|
4月前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现