SpringBoot_7:用户资源的上传与修改

目录

一、Jar包的内部结构

二、建立外部资源目录

三、手动映射外部资源目录

1、使用static-locations配置项

[2、使用WebMvcConfigurer 配置](#2、使用WebMvcConfigurer 配置)

四、项目部署后资源文件上传

五、总结


在以往的项目中,我们把图片,视频等资源都放在static目录下。这种资源被成为静态资源,这种资源的特点是不能被动态写入和修改。

例如,用户希望上传和更换头像,首先把资源的路径以字符串的形式存在数据库中,除此之外还需要把真实资源(例如图片,音频,视频等)存放在外部目录里。不能存在在static下,那么一个SpringBoot项目是如何维护和管理这些资源的呢?本节我们就以用户上传头像为例,讲解这个过程。

一、Jar包的内部结构

在开始本节之前我们需要搞懂项目打包后,代码、配置文件,静态资源、依赖...被打包到哪里,能不能读写。

因此我们先尝试打包一个项目,然后观察Jar包的结构。

使用如下指令来查看jar包的目录结构

bash 复制代码
jar tf my-app-1.0.0.jar

能看到如下目录信息

项目越大文件越多,目录就越复杂。我们可以简单的总结成如下目录:

可以发现除了pom文件被打包到jar包里,还有一些外部库,src里的所有文件都被打包进去了。

也就是说,如果我们建立一个与src同级的目录,在打包时默认会被忽略

META-INF是这个jar包的"身份证明",BOOT-INF则是这个jar包的所有系统文件,可以运行,这个目录下的所有文件都是只读状态,static在这个目录下,因此只读不可写。

二、建立外部资源目录

我们说用户资源不应该放在src/main/resources/static目录下,需要新建一个外部目录,我们计划放到uploads/avatars下。

那么我们先建立目录。

现在我们加入一张照片,看是否可以访问到。

我们尝试这种方式能否访问:

http://localhost:8080/uploads/avatars/img.png![](https://i-blog.csdnimg.cn/direct/434ce90f5817442ca1f9dfa4d901df08.png)

可以发现,无法访问。

这是因为这个目录不是静态资源的目录,SpringBoot项目默认static下的文件可以直接访问,而非static下的资源无法直接访问。

例如,在static下有一张照片,我们尝试访问。

http://localhost:8080/photo/avatar/animal1.jpg就可以访问到。

因此我们还需要把这个目录作为资源目录,对外暴露。

三、手动映射外部资源目录

1、使用static-locations配置项

我们首先考虑使用配置文件来映射外部资源,在application.yaml文件中做如下配置:

XML 复制代码
spring:
  web:
    resources:
      static-locations: file:./uploads/, classpath:/static/

然后我们这样访问:http://localhost:8080/avatars/img.png,为什么不是http://localhost:8080/uploads/avatars/img.png呢?这些参数有什么意义?等会我们会详细讲解。

可以发现,这样确实访问成功了,现在我们解决为什么这样做就可以。

但是我们发现我们配置的是静态资源,static-location,一个是file,一个是classpath,前者指向项目所在的磁盘上的文件,后者指向jar内部的文件。这是两个不同的位置。

file:后可以指向磁盘的任意位置,例如file:D:/myfiles/,也可以相对路径:file:./myfiles ,注意**./**表示项目的根目录(pom文件所在的目录)。

classpath:后则是jar包内部的目录,从BOOT-INF/classes/开始,那么/就表示这个目录,所以从/开始指定static目录为静态资源。

现在只是做到了让外部访问,那么后端能否读写呢?关键看是否可以写,只有这样用户才能上传照片。

我们写如下测试代码:

XML 复制代码
@PostMapping("/modifyAvatars")
    public ApplicationResult<Void> modifyAvatar(MultipartFile file) {
        // 拿到工作目录和file:./配合
        String dir = System.getProperty("user.dir") + "/uploads/avatars/";
        File saveFile = new File(dir+"test.png");
        try {
            file.transferTo(saveFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return ApplicationResult.success();
    }

这是要使用System.getProperty("user.dir")来拿到工作目录。

利用MutipartFile的transferTo(newFile)来保存文件。

我们又想,这种做法只是把/uploads和/static都映射到访问目录/了而已,假如两个目录下有同一张照片img.jpg,这样后期访问的时候就只能看配置顺序了,在访问层面完全无法区分。

http://localhost:8080/img.jpg 无法确认到底是/static/img.jpg还是/uploads/img.jpg

因此这种做法好像是不可行的。

如果我们能从访问路劲上加上区分就好了,不用动后端的代码。

2、使用WebMvcConfigurer 配置

我们删除掉原来的配置,自定义如下配置:

XML 复制代码
# application.yml - 只放配置参数,不做静态资源映射
app:
  upload:
    avatar-path: ./uploads/avatars/
    attachment-path: ./uploads/attachments/
    temp-path: ./uploads/temp/

这里的配置是我们自己定义的,方便我们后续读取这个配置和修改。

avatar-path就是头像地址,设置正确后,实现WebMvcConfigurer

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    // 读取配置文件里的配置
    @Value("${app.upload.avatar-path}")
    private String avatarUrl ;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/avatars/**")
                .addResourceLocations(avatarUrl);
    }
}

addResourceHandler("/")从访问目录的根目录开始,只要匹配到/avatars就把目录指定到file./upload/avatars这样就可以实现只需要在访问路径上做区分,后端文件系统一点都不用变,如下图:

原来的static目录下的文件依然可以访问,因此完全不用担心。

这样我们就实现了用户上传资源到服务器了,数据库只需要存当时写入的文件路径的相对路径即可。数据库不需要存放过多的上层目录,避免暴露过多细节。

例如,在没有做访问路径映射前,数据库可能存放:uploads/avatars/img.jpg

但是做了路径映射后,数据库就可以只存放:avatars/img.jpg,甚至只存放img.jpg,前缀由前端自己拼接。

四、项目部署后资源文件上传

我们前面说,src外出了pom和外部库,其他文件都是不会被一起打包的,因此我们需要手动压缩资源文件,并且解压到和jar的同级目录处(./工作目录也是jar包的目录)。

如果没有把资源压缩并上传到指定目录,只运行Jar包是访问不了资源的。

我们先将资源压缩成压缩包上传到jar包同级目录处

紧接着解压。

bash 复制代码
unzip uploads.zip

然后再次访问即可。

这样资源就可以访问成功了。

五、总结

本节我们以用户上传头像为例,讲解了非静态资源如何读写的问题。

最佳实践是对访问路径进行映射,这样不需要改动后端的文件目录。

对于非静态资源在项目打包时不会打进Jar包,需要手动上传到jar同级目录。

相关推荐
user_admin_god1 小时前
一体化数据归集接口详细设计说明
java·大数据·spring boot·后端·spring
明月_清风1 小时前
十大经典排序算法 Go 实现全解:从入门到面试通关
后端·算法·排序算法
LlmCraft|大模型工程实践2 小时前
08. Docker Compose 一键编排:多容器应用的指挥家
java·spring cloud·eureka
珍珠先生2 小时前
第14章 设计模式入门:单例、工厂、建造者与代理
java
leeyi2 小时前
把规则搬回家:三个 BC 的贫血→充血重构实录(第103篇)
后端
坐吃山猪2 小时前
JDK 8 到 JDK 21 新特性知识要点
java·开发语言·windows
坐吃山猪2 小时前
【多线程】FutureTask多线程底层实现
java·开发语言·数据库
weixin199701080162 小时前
[特殊字符]《从0到1:闲鱼开放平台授权登录 + AccessToken 刷新 + 聚石塔部署完整链路》(附Python源码)
java·数据库·python
学长毕业设计2 小时前
基于SpringBoot的旅游美食攻略分享系统的设计与实现(源码+文档+讲解视频)
spring boot·旅游·美食