spring boot 13

一、文件上传代码修正与完整实现

你贴的代码里有几个明显的问题:

  1. originalFilename.substring(originalFilename.lastIndexOf(".")) 里的 "." 被 IDE 提示标红了,是因为代码里混入了非纯文本字符(比如中文引号或多余空格)。
  2. 路径硬编码、文件不存在判断、返回 URL 都不规范。

下面是修正后的完整版本:

java

运行

复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

@RestController
public class FileUploadController {

    // 从配置文件读取存储路径,避免硬编码
    @Value("${upload.base-path}")
    private String basePath;

    @PostMapping("/upload")
    public Result<String> upload(MultipartFile file) throws IOException {
        // 1. 校验文件是否为空
        if (file.isEmpty()) {
            return Result.error("文件不能为空");
        }

        // 2. 获取原始文件名
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            return Result.error("文件名无效");
        }

        // 3. 提取文件扩展名(兼容无后缀文件)
        String suffix = "";
        int dotIndex = originalFilename.lastIndexOf(".");
        if (dotIndex > 0) {
            suffix = originalFilename.substring(dotIndex);
        }

        // 4. 生成唯一文件名(UUID + 扩展名)
        String filename = UUID.randomUUID().toString() + suffix;

        // 5. 确保目标目录存在
        File destDir = new File(basePath);
        if (!destDir.exists()) {
            destDir.mkdirs(); // 不存在则创建目录
        }

        // 6. 保存文件到磁盘
        File destFile = new File(basePath + filename);
        file.transferTo(destFile);

        // 7. 返回可访问的文件 URL(示例,需根据部署环境调整)
        String url = "http://localhost:8080/files/" + filename;
        return Result.success(url);
    }
}

application.properties 配置:

properties

复制代码
# 文件上传大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

# 自定义存储路径(末尾必须加 /)
upload.base-path=E:/java项目/files/

MultipartFile 核心方法说明

表格

方法 作用
getOriginalFilename() 获取用户上传的原始文件名
transferTo(File dest) 将文件写入指定磁盘路径
getSize() 获取文件大小(单位:字节)
getBytes() 获取文件内容的字节数组
getInputStream() 获取文件内容的输入流

二、文章列表(条件分页)实现

1. 分页返回对象 PageBean

java

运行

复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean<T> {
    private Long total;       // 总记录数
    private List<T> items;   // 当前页数据列表
}

2. Controller 层接口

java

运行

复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/article")
public class ArticleController {

    private final ArticleService articleService;

    public ArticleController(ArticleService articleService) {
        this.articleService = articleService;
    }

    @GetMapping
    public Result<PageBean<Article>> list(
            @RequestParam Integer pageNum,
            @RequestParam Integer pageSize,
            @RequestParam(required = false) String categoryId,
            @RequestParam(required = false) String state) {

        PageBean<Article> pageBean = articleService.list(pageNum, pageSize, categoryId, state);
        return Result.success(pageBean);
    }
}

3. Service 层实现(使用 PageHelper)

java

运行

复制代码
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;

@Service
public class ArticleServiceImpl implements ArticleService {

    private final ArticleMapper articleMapper;

    public ArticleServiceImpl(ArticleMapper articleMapper) {
        this.articleMapper = articleMapper;
    }

    @Override
    public PageBean<Article> list(Integer pageNum, Integer pageSize, String categoryId, String state) {
        // 1. 开启分页查询
        PageHelper.startPage(pageNum, pageSize);

        // 2. 从 ThreadLocal 获取当前用户ID
        Map<String, Object> claims = ThreadLocalUtil.get();
        Integer userId = (Integer) claims.get("id");

        // 3. 执行条件查询
        List<Article> articleList = articleMapper.list(userId, categoryId, state);

        // 4. 封装分页结果
        PageInfo<Article> pageInfo = new PageInfo<>(articleList);
        PageBean<Article> pageBean = new PageBean<>();
        pageBean.setTotal(pageInfo.getTotal());
        pageBean.setItems(pageInfo.getList());

        return pageBean;
    }
}

4. Mapper 层(XML 动态 SQL)

xml

复制代码
<mapper namespace="com.example.springboot.mapper.ArticleMapper">
    <select id="list" resultType="com.example.springboot.pojo.Article">
        select * from article
        <where>
            create_user = #{userId}
            <if test="categoryId != null and categoryId != ''">
                and category_id = #{categoryId}
            </if>
            <if test="state != null and state != ''">
                and state = #{state}
            </if>
        </where>
    </select>
</mapper>

三、关键注意事项

文件上传

  • 路径硬编码不利于部署,建议通过配置文件读取。
  • 目标目录如果不存在,transferTo 会抛出异常,必须提前创建。
  • 使用 UUID 生成唯一文件名,避免文件覆盖。

分页查询

  • PageHelper.startPage(pageNum, pageSize) 必须紧跟在查询语句之前,否则分页失效。
  • 必须加上 create_user = #{userId} 条件,确保用户只能查询自己的文章。
  • categoryIdstate 是可选参数,使用 @RequestParam(required = false) 声明。

java

运行

复制代码
//获取原始文件名
String originalFilename = file.getOriginalFilename();

//构造唯一文件名(uuid + 文件后缀)
String filename = UUID.randomUUID().toString() + 
                  originalFilename.substring(originalFilename.lastIndexOf("."));

//存储文件
file.transferTo(new File("E:\\java项目\\files\\" + filename));

逐行解释

1. 获取原始文件名

java

运行

复制代码
String originalFilename = file.getOriginalFilename();

意思: 用户上传的文件叫什么,我就拿到什么。比如用户传了一张 cat.jpg,这里得到的就是 "cat.jpg"


2. 生成唯一文件名(最重要!)

java

运行

复制代码
String filename = UUID.randomUUID().toString()
        + originalFilename.substring(
            originalFilename.lastIndexOf(".")
        );

拆成 3 部分讲:

UUID.randomUUID().toString()

作用:生成一串全球唯一的随机字符串比如:

plaintext

复制代码
550e8400-e29b-41d4-a716-446655440000

为什么要用? 防止两个人上传 同名文件 导致覆盖!


originalFilename.lastIndexOf(".")

作用:找到文件名里最后一个 . 的位置

比如:cat.jpg. 在索引 3 的位置


substring( 位置 )

作用:从 . 开始截取到最后 → 拿到文件后缀

cat.jpg → 截取后 → .jpg


✅ 拼起来最终效果:

plaintext

复制代码
唯一字符串 + .后缀

例如:

plaintext

复制代码
550e8400-e29b-41d4-a716-446655440000.jpg

3. 把文件保存到电脑磁盘

java

运行

复制代码
file.transferTo( new File("E:\\java项目\\files\\" + filename) );

作用: 把前端传过来的文件,真正写入到服务器硬盘里

路径示例:

plaintext

复制代码
E:\java项目\files\550e8400-e29b-41d4-a716-446655440000.jpg

✨ 总结(3 句话记住)

  1. 获取原文件名 → 知道用户传的是什么文件
  2. 生成唯一文件名 → 避免文件重名覆盖
  3. 保存到硬盘 → 文件真正存下来

🎯 最关键的 2 个知识点

1. 为什么要用 UUID?

如果不用:

  • A 传 a.jpg → 存成 a.jpg
  • B 也传 a.jpg → 覆盖掉 A 的文件

用了:

  • 每个人的文件名永远不会重复

2. 为什么要截取后缀?

因为要保留文件类型:

  • .jpg 图片
  • .png 图片
  • .mp4 视频
  • .pdf 文件

如果你愿意,我还能给你讲:

  • 怎么把文件存到阿里云 OSS(企业真实用法)
  • 怎么访问上传后的图片
  • 怎么限制文件大小、类型

你要继续吗?

与上文合并

📂 文件上传存储 完整代码 + 逐行解析

java

运行

复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

@RestController
public class FileUploadController {
    // 读取配置文件中的文件存储路径,避免硬编码
    @Value("${upload.base-path}")
    private String basePath;

    @PostMapping("/upload")
    public Result<String> upload(MultipartFile file) throws IOException {
        // 校验空文件
        if (file.isEmpty()){
            return Result.error("文件不能为空");
        }

        // 1.获取原始文件名,包含后缀格式,例:photo.jpg
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null){
            return Result.error("文件名异常");
        }

        // 2.拆分文件名与后缀,兼容无后缀文件
        String suffix = "";
        int index = originalFilename.lastIndexOf(".");
        if(index > 0){
            // 截取最后一个点之后的内容,拿到文件后缀
            suffix = originalFilename.substring(index);
        }

        // 3.生成全新唯一文件名
        // UUID生成不重复随机字符串,拼接后缀,防止同名文件相互覆盖
        String filename = UUID.randomUUID().toString() + suffix;

        // 4.判断并创建存储文件夹
        File folder = new File(basePath);
        if(!folder.exists()){
            folder.mkdirs();
        }

        // 5.拼接完整文件路径,将文件写入本地磁盘保存
        File targetFile = new File(basePath + filename);
        file.transferTo(targetFile);

        // 返回文件访问地址
        String url = "http://localhost:8080/files/" + filename;
        return Result.success(url);
    }
}

配套 yml/properties 配置

properties

复制代码
# 限制上传文件大小
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
# 文件存放目录
upload.base-path=E:/java项目/files/

核心代码释义

  1. **getOriginalFilename()**获取用户本地上传的原始文件全名,包含后缀名,用于后续拆分格式。

  2. lastIndexOf + substring定位文件名最后一个小数点,截取后缀部分,保留文件格式属性。

  3. **UUID.randomUUID()**生成全局唯一字符串,拼接后缀组成新文件名,彻底解决文件重名覆盖问题。

  4. **mkdirs()**检测存储目录是否存在,不存在则自动创建目录,避免保存时报错。

  5. **transferTo()**接收前端上传的文件流,写入指定磁盘路径,完成文件持久化存储。

相关推荐
marlondu2 小时前
ScopedValue:Java 21 引入的结构化作用域值
java
risc1234562 小时前
DocumentsWriterDeleteQueue
java·开发语言
日月云棠2 小时前
12 Dubbo 2.7 服务发布全流程源码解析
java·后端
WAIT_TIME2 小时前
基于 Docker 快速构建 MySQL InnoDB Cluster 高可用集群与 Router 读写分离
mysql·docker·集群·innodb cluster
Full Stack Developme2 小时前
SQL like 与 正则 区别
数据库·sql·mysql
用户298698530142 小时前
告别手动复制:Java 拆分 Word 文档的两种实用方案
java·后端
ujainu小2 小时前
CANN hixl:大模型 PD 分离场景的零拷贝通信库
android·java·缓存
z200509302 小时前
今日算法(组合问题III)(回溯的使用)
java·算法·leetcode
XiYang-DING2 小时前
【Java EE】IPv6
java·java-ee·php