【尚庭公寓SpringBoot + Vue 项目实战】图片上传(十)

news2024/11/24 13:01:06

【尚庭公寓SpringBoot + Vue 项目实战】图片上传(十)


文章目录

      • 【尚庭公寓SpringBoot + Vue 项目实战】图片上传(十)
        • 1、图片上传流程
        • 2、图片上传接口查看
        • 3、代码开发
          • 3.1、配置Minio Client
          • 3.2、开发上传图片接口
        • 4、异常处理

1、图片上传流程

​ 由于公寓、房间等实体均包含图片信息,所以在新增或修改公寓、房间信息时,需要上传图片,因此我们需要实现一个上传图片的接口。

下图展示了新增房间或公寓时,上传图片的流程。

image-20240614211618302

可以看出图片上传接口接收的是图片文件,返回的Minio对象的URL

2、图片上传接口查看

image-20240614211817521

接口信息:

  • 请求地址: /admin/file/upload
  • 请求类型: POST
  • 请求内容类型: multipart/form-data

请求参数:

参数名称参数说明请求类型是否必须数据类型schema
file文件querytruefile

响应状态:

状态码说明
200OK

响应参数:

参数名称类型参数说明
codeinteger(int32)返回码
messagestring返回信息
datastring数据

响应示例:

{
  "code": 0,
  "message": "",
  "data": ""
}

解释:

  • 请求地址:此接口的请求地址是 /admin/file/upload,用于上传文件。
  • 请求类型POST 表示这是一个 POST 请求。
  • 请求内容类型multipart/form-data 表示请求数据需要以多部分表单数据格式发送。
  • 请求参数
    • file:文件,必须提供,用于上传的文件。
  • 响应状态:200 表示请求成功。
  • 响应参数
    • code:返回码,整数类型,表示请求的状态。
    • message:返回信息,字符串类型,描述请求的结果。
    • data:数据,字符串类型,通常包含上传文件的相关信息或 URL。
3、代码开发
3.1、配置Minio Client

引入依赖

common模块pom.xml文件增加如下内容:

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
</dependency>

配置Minio相关参数

application.yml中配置Minio的endpointaccessKeysecretKeybucketName等参数

minio:
  endpoint: http://<hostname>:<port>
  access-key: <access-key>
  secret-key: <secret-key>
  bucket-name: <bucket-name>

**注意:**上述<hostname><port>等信息需根据实际情况进行修改。

common模块中创建com.atguigu.lease.common.minio.MinioProperties,内容如下

@ConfigurationProperties(prefix = "minio")
@Data
public class MinioProperties {

    private String endpoint;

    private String accessKey;

    private String secretKey;
    
    private String bucketName;
}

common模块中创建com.atguigu.lease.common.minio.MinioConfiguration,内容如下

@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinioConfiguration {

    @Autowired
    private MinioProperties properties;

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder().endpoint(properties.getEndpoint()).credentials(properties.getAccessKey(), properties.getSecretKey()).build();
    }
}
3.2、开发上传图片接口

编写Controller层逻辑

FileUploadController中增加如下内容

@Tag(name = "文件管理")
@RequestMapping("/admin/file")
@RestController
public class FileUploadController {

    @Autowired
    private FileService service;

    @Operation(summary = "上传文件")
    @PostMapping("upload")
    public Result<String> upload(@RequestParam MultipartFile file) {

        String url = service.upload(file);
        return Result.ok(url);
    }
}

说明:MultipartFile是Spring框架中用于处理文件上传的类,它包含了上传文件的信息(如文件名、文件内容等)。

编写Service层逻辑

FileService中增加如下接口

String upload(MultipartFile file);

FileServiceImpl中增加如下内容

@Autowired
private MinioProperties properties;

@Autowired
private MinioClient client;

@Override
public String upload(MultipartFile file) {

    try {
        boolean bucketExists = client.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucketName()).build());
        if (!bucketExists) {
            client.makeBucket(MakeBucketArgs.builder().bucket(properties.getBucketName()).build());
            client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(properties.getBucketName()).config(createBucketPolicyConfig(properties.getBucketName())).build());
        }

        String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
        client.putObject(PutObjectArgs.builder().
                bucket(properties.getBucketName()).
                object(filename).
                stream(file.getInputStream(), file.getSize(), -1).
                contentType(file.getContentType()).build());

        return String.join("/", properties.getEndpoint(), properties.getBucketName(), filename);

    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

private String createBucketPolicyConfig(String bucketName) {

    return """
            {
              "Statement" : [ {
                "Action" : "s3:GetObject",
                "Effect" : "Allow",
                "Principal" : "*",
                "Resource" : "arn:aws:s3:::%s/*"
              } ],
              "Version" : "2012-10-17"
            }
            """.formatted(bucketName);
}

由于公寓、房间的图片为公开信息,所以将其设置为所有人可访问。

4、异常处理

问题说明

上述代码只是对MinioClient方法抛出的各种异常进行了捕获,然后打印了异常信息,目前这种处理逻辑,无论Minio是否发生异常,前端在上传文件时,总是会受到成功的响应信息。可按照以下步骤进行操作,查看具体现象

关闭虚拟机中的Minio服务

systemctl stop minio

启动项目,并上传文件,观察接收的响应信息

问题解决思路

为保证前端能够接收到正常的错误提示信息,应该将Service方法的异常抛出到Controller方法中,然后在Controller方法中对异常进行捕获并处理。具体操作如下

Service层代码

@Override
public String upload(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException{

    boolean bucketExists = minioClient.bucketExists(
            BucketExistsArgs.builder()
                    .bucket(properties.getBucketName())
                    .build());
    if (!bucketExists) {
        minioClient.makeBucket(
                MakeBucketArgs.builder()
                        .bucket(properties.getBucketName())
                        .build());
        minioClient.setBucketPolicy(
                SetBucketPolicyArgs.builder()
                        .bucket(properties.getBucketName())
                        .config(createBucketPolicyConfig(properties.getBucketName()))
                        .build());
    }
    String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) +
            "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
    minioClient.putObject(
            PutObjectArgs.builder()
                    .bucket(properties.getBucketName())
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .object(filename)
                    .contentType(file.getContentType())
                    .build());

    return String.join("/",properties.getEndpoint(),properties.getBucketName(),filename);
}

Controller层代码

public Result<String> upload(@RequestParam MultipartFile file) {
    try {
        String url = service.upload(file);
        return Result.ok(url);
    } catch (Exception e) {
        e.printStackTrace();
        return Result.fail();
    }
}

全局异常处理

按照上述写法,所有的Controller层方法均需要增加try-catch逻辑,使用Spring MVC提供的全局异常处理功能,可以将所有处理异常的逻辑集中起来,进而统一处理所有异常,使代码更容易维护。

具体用法如下,详细信息可参考官方文档:

common模块中创建com.atguigu.lease.common.exception.GlobalExceptionHandler类,内容如下

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.fail();
    }
}

上述代码中的关键注解的作用如下

@ControllerAdvice用于声明处理全局Controller方法异常的类

@ExceptionHandler用于声明处理异常的方法,value属性用于声明该方法处理的异常类型

@ResponseBody表示将方法的返回值作为HTTP的响应体

注意:

全局异常处理功能由SpringMVC提供,因此需要在common模块pom.xml中引入如下依赖

<!--spring-web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

修改Controller层代码

由于前文的GlobalExceptionHandler会处理所有Controller方法抛出的异常,因此Controller层就无序关注异常的处理逻辑了,因此Controller层代码可做出如下调整。

public Result<String> upload(@RequestParam MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
    String url = service.upload(file);
  return Result.ok(url);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1828045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

12.泛型、trait和生命周期(中)

标题 五、定义共享的行为5.1 定义trait5.2 实现trait5.4 将trait作为参数5.5 Trait Bound5.6 返回实现了trait的类型5.7 使用trait bounds修复get_max_value_from_vector的错误5.8 使用trait bound有条件地实现方法 五、定义共享的行为 如果告诉Rust编译器某个特定类型拥有可能…

【办公类-04-03】华为助手导出照片视频分类(根据图片、视频的文件名日期分类导出)

背景需求&#xff1a; 用华为手机助手导出的照片视频&#xff0c;只能将jpg照片&#xff08;exifread读取图片的exif拍摄日期&#xff0c;Png、JPEG、mp4都无法识别到exif信息&#xff09; 【办公类-04-02】华为助手导出照片&#xff08;jpg&#xff09;读取拍摄时间分类导出…

Linux——ansible的应用

要让ansible管理业务里的主机 1.得先知道&#xff0c;有哪些主机 用IP地址&#xff0c;用主机名 2.知道了有哪些主机以后&#xff0c;精细、细分管理 主机要用某些办法&#xff0c;分组管理 在ansible里&#xff0c;要用一个东西&#xff1a;清单->inventory inventory …

C学习自学笔记-会陆续完善对应章节编程经典例子

C学习笔记 0>C语言概述 为什么学习C语言 1&#xff09;C的起源和发展------了解即可 B语言、C语言、C语言的产生地&#xff1a;都出自 美国贝尔实验室 2&#xff09;C的特点 优点&#xff1a;代码量小、速度快、功能强大 缺点&#xff1a;危险性高、开发周期长、可移植性…

POC EXP | woodpecker插件编写

woodpecker插件编写 目录 woodpecker介绍woodpecker使用插件编写 安装环境 woodpecker-sdkwoodpecker-request 创建Maven项目 Confluence OGNL表达式注入漏洞插件编写 创建Package包和Class类编写POC 漏洞POC代码编写导出jar包将jar包放入woodpecker的plugin目录运行woodpeck…

2.2 抽头

目录 为什么要抽头 什么是抽头 接入系数 怎么抽头 信号源端抽头 负载端抽头 例题分析 要点总结 为什么要抽头 阻抗转换&#xff0c;使信号源内阻Rs与负载电阻RL变得很大&#xff0c;分流小&#xff0c;再使用并联方式。 什么是抽头 接入系数 电容越大&#xff0c;分压越…

Hadoop 2.0:主流开源云架构(四)

目录 五、Hadoop 2.0访问接口&#xff08;一&#xff09;访问接口综述&#xff08;二&#xff09;浏览器接口&#xff08;三&#xff09;命令行接口 六、Hadoop 2.0编程接口&#xff08;一&#xff09;HDFS编程&#xff08;二&#xff09;Yarn编程 五、Hadoop 2.0访问接口 &am…

最大连续子序列和问题详解

最大连续子序列和问题如下&#xff1a;给定一个数字序列&#xff0c;求i,j&#xff0c;使得最大&#xff0c;输出这个最大和。 这个问题如果用暴力来做&#xff0c;枚举左端点和右端点&#xff0c;需要的复杂度&#xff0c;而计算需要的复杂度&#xff0c;因此总时间复杂度为。…

表面声波滤波器——压电材料(2)

声表面波压电材料 压电效应&#xff1a; 1880年&#xff0c;法国物理学家居里兄弟(PCunie 和J.Curie)发现将重物置于α石英晶体上,品体部分表面会产生电荷&#xff0c;电荷量与所受压力成正比正压电效应&#xff1a;外加压力作应下在表面间产生电位差。 和逆压电效&#xff1a…

14 学习PID--步进电机梯形加减速实现原理

步进电机加减速使用的场景有那些呢&#xff1f;为什么要使用加减速呢&#xff1f; 硬件驱动细分器与软件的细分参数或定时器分频参数设置不当时启动电机时&#xff0c;会遇见步进电机有啸叫声但是不会转动&#xff0c;这是因为软件产生脉冲的频率大于步进电机的启动频率&#x…

Iptables深入浅出

1、iptables的基本概念 众所周知iptables是Linux系统下自带免费的包过滤防火墙。其实不然&#xff0c;iptables其实不是真正的防火墙&#xff0c;我们可以把它理解成一个客户端代理&#xff0c;用户通过iptables这个代理&#xff0c;将用户的安全设定执行到对应的”安全框架”…

【Oracle APEX开发小技巧1】转换类型实现显示小数点前的 0 以 及常见类型转换

在 apex 交互式式网格中&#xff0c;有一数值类型为 NUMBER&#xff0c;保留小数点后两位的项&#xff0c;在 展示时小数点前的 0 不显示。 效果如下&#xff1a; 转换前&#xff1a; m.WEIGHT_COEFFICIENT 解决方案&#xff1a; 将 NUMBER&#xff08;20&#xff0c;2&#xf…

Python进阶:从函数到文件的编程艺术!!!

第二章&#xff1a;Python进阶 模块概述 函数是一段可重复使用的代码块&#xff0c;它接受输入参数并返回一个结果。函数可以用于执行特定的任务、计算结果、修改数据等&#xff0c;使得代码更具模块化和可重用性。 模块是一组相关函数、类和变量的集合&#xff0c;它们被封…

Android Media Framework(六)插件式编程与OMXStore

OpenMAX IL Spec阅读到上一节就结束了&#xff0c;这一节开始正式进入到Framework阅读阶段&#xff0c;我们将了解OpenMAX框架是如何与Android Framework连接的。 1、插件式编程 插件式编程&#xff08;Plugin-based Programming&#xff09;是一种软件开发模式&#xff0c;它…

docker login 报错: http: server gave HTTP response to HTTPS client

环境&#xff1a; 自建 Harbor、Docker 1. 问题分析 # 命令&#xff0c;这里用的是 IP&#xff0c;可以为域名 docker login -u test 172.16.51.182:31120 # 输入密码 Password:# 报错如下&#xff1a; Error response from daemon: Get "https://172.16.51.182:31120/…

Flutter IOS 打包上架踩坑

前言 Flutter 作为一款跨平台的移动应用开发框架&#xff0c;凭借其高效、灵活和美观的特性&#xff0c;受到了越来越多开发者的青睐。 然而&#xff0c;当开发者们倾注心血完成 Flutter iOS 应用开发后&#xff0c;如何将应用成功上架至苹果商店&#xff08;App Store&#…

关于QTcreator,19年大学时写的文章了,之前写在印象笔记现在拉过来,往事如烟呐

1.初来乍到&#xff0c;先按照书本写一个基础列程理解一下原理。 这里创建工程的时候选择Qdialog基类&#xff0c;dialog.h头文件&#xff0c;并且勾选了创建界面 &#xff08;勾选之后可以通过手动添加组块并且可以自生成他们的函数定义&#xff0c;如果没有勾选&#xff0c;…

R 初级教程之一

IT的发展目前已经相当的内卷&#xff0c;到处都在说24年是将来4年最难的一年&#xff01;确实是&#xff0c;眼下各大厂商都在疯狂的裁员砍掉不营利的业务&#xff0c;收紧业务&#xff0c;不再盲目的扩张。小公司更是水深火热&#xff0c;无以言表。近期有个医院联系让使用R给…

uniapp中unicloud接入支付宝订阅消息完整教程

经过无数次的尝试,终于还是让我做出来了 准备工作 设置接口加签方式 使用支付宝小程序订阅消息,首先要设置接口加签方式,需要下载支付宝开放平台密钥工具,按照步骤生成秘钥,然后按照支付宝设置密钥加签方式添加接口加签方式。 有一点需要注意的,因为要在云函数中使用,…

MyBatis源码--04:MyBatis缓存实现

缓存作用&#xff1a; 在程序访问数据库这个过程中&#xff0c;存在几个性能瓶颈&#xff1a; 网络通信非关系型数据库将数据存储在硬盘当中&#xff0c;需要进行硬盘操作Java对象复用问题&#xff08;Connection连接池&#xff0c;Statement对象&#xff09; 缓存在程序和数…