基于 Spring Boot 实现图片的服务器本地存储及前端回显

🎯导读:本文探讨了在网站开发中图片存储的各种方法,包括本地文件系统存储、对象存储服务(如阿里云OSS)、数据库存储、分布式文件系统及内容分发网络(CDN)。文中详细对比了这些方法的优缺点,并针对不同规模的应用推荐了相应的存储策略。此外,还提供了一个基于SpringBoot框架实现服务器本地图片存储与回显的具体示例,涵盖了从图片上传到通过网关服务安全访问图片的全过程。

文章目录

  • 前言
    • 文件存储分类
      • 本地文件系统存储
      • 对象存储服务(OSS)
      • 数据库存储
      • 分布式文件系统
      • 内容分发网络(CDN)
      • 总结
  • 服务器本地图片存储实现
    • 图片上传实现
      • Gateway 服务
        • 将图片访问链接添加到白名单,跳过登录验证
        • 过滤器
      • Admin 服务
        • 依赖
        • Controller
        • Service
          • impl
        • 错误码枚举类
        • 工具类
    • 图片回显
      • 静态资源处理
    • 功能测试

前言

在开发网站时,存储图片是常见的需求之一,尤其对于社交平台、电子商务网站等应用。例如社交平台要存储用户头像,要存储用户发布的朋友圈图片,聊天记录图片等等

文件存储分类

为了满足不同场景下的需求,图片存储有多种常见的方式,每种方式都有其适用场景和特点。以下是几种常用的图片存储方式及其优缺点分析

本地文件系统存储

描述:将图片直接保存在Web服务器的本地文件系统中,通常会有一个固定的目录用于存放这些图片文件。

优点

  • 简单易用:实现起来非常简单,适合小型项目快速搭建或者图片较少的项目。
  • 成本低廉:初期成本较低,不需要额外支付存储费用。

缺点

  • 扩展性差:随着图片数量的增加,服务器存储空间会变得紧张,需要频繁地扩容。
  • 备份困难:需要手动进行备份,且容易出现单点故障。
  • 安全性低:服务器直接暴露在互联网上,存在安全隐患。
  • 访问速度受限:图片访问速度受限于服务器带宽,如果有多人同时访问,部分用户需要等待。

对象存储服务(OSS)

描述:使用云服务商提供的对象存储服务,如阿里云OSS、AWS S3、Google Cloud Storage等,将图片存储在云端。

优点

  • 高可用性和持久性:提供多副本冗余,确保数据的高可用性和持久性。
  • 易于扩展:可以根据实际需求动态调整存储空间,无需担心物理存储限制。
  • 安全性高:提供多种安全机制,如访问控制、数据加密等。
  • 全球分布:支持全球多区域部署,提高用户访问速度。
  • 成本效益:按需付费,适合业务量波动较大的应用。

缺点

  • 成本随业务增长而增加:随着存储和带宽的增加,费用也会相应增加。
  • 访问速度受网络影响:网络状况不佳时,图片加载速度可能变慢。
  • 依赖第三方服务:需要考虑服务商的服务质量和稳定性。
  • 需要严格控制图片安全:如果没有严格做好防控措施,图片一旦被有心之人频繁刷量,会造成巨大的费用

数据库存储

描述:将图片以二进制形式存储在数据库中,通常会使用BLOB(Binary Large Object)字段。

优点

  • 事务一致性:图片和相关数据可以保持事务一致性,适合需要强一致性的应用场景。
  • 便于管理:图片和元数据可以集中管理,方便查询和操作。

缺点

  • 性能较差:读取和写入大文件时性能较差,可能会影响数据库的整体性能。
  • 存储成本高:数据库存储成本较高,不适合大量图片的存储。
  • 备份和恢复复杂:数据库备份和恢复过程较为复杂,且占用较多资源。

分布式文件系统

描述:使用分布式文件系统(如Hadoop HDFS、GlusterFS等)来存储图片,适合大规模分布式存储需求。

优点

  • 高可用性和扩展性:支持横向扩展,可以通过增加节点来提升存储能力和访问性能。
  • 容错能力强:具有良好的容错能力,可以自动处理节点故障。
  • 成本效益:相对于传统存储方式,成本更低,适合大规模数据存储。

缺点

  • 复杂度高:实现和维护相对复杂,需要专业的运维团队。
  • 访问速度受网络影响:网络状况不佳时,访问速度可能受影响。
  • 学习曲线陡峭:需要一定的学习成本和技术积累。

内容分发网络(CDN)

描述:结合对象存储服务和CDN,将图片存储在云存储中,并通过CDN加速分发,提高全球用户的访问速度。

优点

  • 全球加速:通过CDN缓存,提高全球用户的访问速度。
  • 高可用性和持久性:结合对象存储服务,确保数据的高可用性和持久性。
  • 成本效益:按需付费,适合业务量大的应用。

缺点

  • 成本较高:使用CDN会增加一定的成本。
  • 配置复杂:需要配置CDN和对象存储服务,相对复杂。
  • 也要严格做好防控措施,否则钱包非常危险

总结

选择合适的图片存储方式需要综合考虑项目的规模、预算、性能要求和未来的扩展性。对于小型项目或初创团队,可以先从本地文件系统存储开始,随着业务的增长逐步迁移到对象存储服务或分布式文件系统。对于大型应用或全球用户较多的场景,建议使用对象存储服务结合CDN,以确保高性能和高可用性。

服务器本地图片存储实现

对于个人开发的小网站,如果图片不多,网站访问量也不多,使用服务器本地存储的方式是比较适合的,本文主要讲解SpringBoot项目如何实现图片如何存储到服务器本地以及前端要怎样才能显示服务器中的图片资源

演示项目说明:该项目使用微服务架构开发,请求首先需要通过Gateway网关,然后转给对应的Admin服务,Admin服务中再处理图片的存储和回显

图片上传实现

Gateway 服务

将图片访问链接添加到白名单,跳过登录验证
spring:
  profiles:
    active: damWin
  application:
    name: vrs-gateway
  cloud:
    gateway:
      routes:
        - id: vrs-admin
          uri: lb://vrs-admin/admin/**
          predicates:
            - Path=/admin/**
          filters:
            - name: TokenValidate
              args:
                whitePathList:
                  - /admin/user/v1/login
                  - /admin/user/v1/has-username
                  - /admin/user/v1/register
                  - /admin/pic/
过滤器

拦截请求,将网关服务的访问域名放置到请求头中,这样Admin服务就可以在请求头中拿到网关服务的访问域名了

package com.vrs.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @Author dam
 * @create 2024/11/21 20:55
 */

@Component
public class CustomGlobalFilter {
    @Value("${server.port}")
    private int serverPort;

    /**
     * 将网关服务的域名端口放到请求头中,方便其他服务使用
     * @return
     */
    @Bean(name = "customGlobalFilter1")
    public GlobalFilter customGlobalFilter() {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String hostAndPort = getHostAndPort(request);
            ServerHttpRequest modifiedRequest = request.mutate()
                    .header("X-Gateway-Host", hostAndPort)
                    .build();

            ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();
            return chain.filter(modifiedExchange);
        };
    }

    /**
     * 获取网关服务的访问域名端口
     * @param request
     * @return
     */
    private String getHostAndPort(ServerHttpRequest request) {
        try {
            // 获取请求的主机名和端口
            String host = request.getURI().getHost();
            if (host == null) {
                // 如果请求中没有主机名,使用本地主机名
                host = InetAddress.getLocalHost().getHostName();
            }
            // 获取请求的协议(HTTP或HTTPS)
            String scheme = request.getURI().getScheme();
            return scheme + "://" + host + ":" + serverPort;
        } catch (UnknownHostException e) {
            return "unknown";
        }
    }
}

Admin 服务

依赖
<!-- 图片压缩工具 -->
<dependency>
    <groupId>net.coobird</groupId>
    <artifactId>thumbnailator</artifactId>
    <version>0.4.8</version>
</dependency>
Controller

注意:里面有一步是从请求头中获取网关的访问域名,后面再基于网关的访问域名来拼接图片访问路径,不能直接将Admin服务的域名暴露出去,不然用户可能直接绕过网关的登录验证来访问Admin服务

package com.vrs.controller;

import com.vrs.convention.result.Result;
import com.vrs.service.PictureService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

/**
 * @Author dam
 * @create 2024/11/21 17:00
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/pic/")
public class PictureController {

    private final PictureService pictureService;

    /**
     * 通用上传请求(单个)
     */
    @PostMapping("/upload")
    public Result<Map<String, Object>> uploadFile(MultipartFile file, HttpServletRequest request) throws Exception {
        String gatewayHost = request.getHeader("X-Gateway-Host");

        return pictureService.uploadFile(file, gatewayHost);
    }
}
Service
package com.vrs.service;

import com.vrs.convention.result.Result;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

/**
 * @Author dam
 * @create 2024/11/21 17:12
 */
public interface PictureService {
    Result<Map<String, Object>> uploadFile(MultipartFile file, String request);
}
impl
package com.vrs.service.impl;

import com.vrs.common.utils.PictureUploadUtil;
import com.vrs.convention.exception.ClientException;
import com.vrs.convention.result.Result;
import com.vrs.convention.result.Results;
import com.vrs.service.PictureService;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.Map;

import static com.vrs.common.enums.ErrorCodeEnum.PICTURE_UPLOAD_FAIL;

/**
 * @Author dam
 * @create 2024/11/21 17:12
 */
@Service
public class PictureServiceImpl implements PictureService {

    @Override
    public Result<Map<String, Object>> uploadFile(MultipartFile file, String gatewayDomain) {
        // 上传并返回新文件名称
        String fileName = PictureUploadUtil.upload(file);
        String url = gatewayDomain + "/admin" + fileName;
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("url", url);
        resultMap.put("fileName", fileName);
        resultMap.put("originalFileName", file.getOriginalFilename());
        return Results.success(resultMap);
    }
}
错误码枚举类
package com.vrs.common.enums;

import com.vrs.convention.errorcode.IErrorCode;

/**
 * 用户错误码
 */
public enum ErrorCodeEnum implements IErrorCode {

    PICTURE_UPLOAD_FAIL("B000301", "图片上传失败"),
    PICTURE_NAME_EXCEED_LENGTH("B000302", "图片名超出长度"),
    PICTURE_SUFFIX_ERROR("B000302", "图片名没有携带正常后缀名"),
    PICTURE_TYPE_ERROR("B000302", "图片格式不对,仅限于 .png .jpg .jpeg .gif"),

    ;

    private final String code;

    private final String message;

    ErrorCodeEnum(String code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public String code() {
        return code;
    }

    @Override
    public String message() {
        return message;
    }
}
工具类

该工具类主要完成对图片的校验,然后将图片存储到服务器本地,最后返回图片的访问路径

package com.vrs.common.utils;

import com.vrs.convention.exception.ClientException;
import com.vrs.convention.exception.ServiceException;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.Objects;
import java.util.UUID;

import static com.vrs.common.enums.ErrorCodeEnum.*;

/**
 * @Author dam
 * @create 2024/11/21 17:18
 */
public class PictureUploadUtil {
    /**
     * 资源映射路径 前缀
     */
    public static final String PIC_PREFIX = "/pic";
    /**
     * 图片存储的根路径
     */
    public static final String UPLOAD_PATH = System.getProperty("user.dir") + File.separator + "upload";
    /**
     * 默认的文件名最大长度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

    public static final String upload(MultipartFile file) {
        int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
        if (fileNameLength > DEFAULT_FILE_NAME_LENGTH) {
            // --if-- 如果图片名称过程,抛异常
            throw new ClientException(PICTURE_NAME_EXCEED_LENGTH);
        }

        String name = file.getOriginalFilename();
        if (!name.contains(".")) {
            // --if-- 如果图片没有正常后缀
            throw new ClientException(PICTURE_SUFFIX_ERROR);
        } else if (!(name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".gif"))) {
            // --if-- 校验文件是否为图片类型
            throw new ClientException(PICTURE_TYPE_ERROR);
        }
        String[] split = name.split("\\.");
        // 图片名称
        String fileName = split[0];
        // 图片后缀
        String fileSuffix = split[1];

        // 获取当前日期
        LocalDate date = LocalDate.now();
        // 获取年份
        int year = date.getYear();
        // 获取月份
        int month = date.getMonthValue();
        // 获取日期
        int day = date.getDayOfMonth();
        String dir = File.separator + year + File.separator + month + File.separator + day;
        File dirFile = new File(UPLOAD_PATH + dir);
        if (!dirFile.exists()) {
            // 创建相应日期文件夹
            dirFile.mkdirs();
        }
        // 生成一个唯一ID
        String uuid = UUID.randomUUID().toString().replace("-", "");
        // 相对路径
        String relativePath = dir + File.separator + fileName + "-" + uuid + "." + fileSuffix;
        // 生成图片要上传到的绝对路径
        String absPath = UPLOAD_PATH + relativePath;

        // 压缩存储
//        try (InputStream is = file.getInputStream()) {
//            // 设置目标文件
//            File targetFile = new File(absPath);
//
//            // 使用Thumbnails库调整图片分辨率
//            Thumbnails.of(is)
//                    // 设置最大宽度和高度,保持原始比例
//                    .size(1920, 1080)
//                    .toFile(targetFile);
//        } catch (Exception e) {
//            e.printStackTrace();
//            throw new ServiceException(e.getMessage());
//        }


        // 直接保存文件(不压缩)
        try (InputStream is = file.getInputStream()) {
            File targetFile = new File(absPath);
            // 将输入流中的数据复制到目标文件中
            java.nio.file.Files.copy(is, targetFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }

        return PIC_PREFIX + "/" + year + "/" + month + "/" + day + "/" + fileName + "-" + uuid + "." + fileSuffix;
    }

}

图片回显

图片回显的实现比较简单,添加一个配置类即可

静态资源处理

这段代码的作用是:把网站上显示图片的URL路径(比如 /pic/**)映射到服务器上的一个实际文件夹路径。这样设置后,用户通过浏览器访问这些URL时,就能直接看到存储在服务器指定文件夹里的图片

import com.vrs.common.utils.PictureUploadUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 通用配置
 *
 * @author dam
 */
@Configuration
public class ResourcesConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 将图片请求路径映射到对应的本地图片路径
        registry.addResourceHandler(PictureUploadUtil.PIC_PREFIX + "/**")
                .addResourceLocations("file:" + PictureUploadUtil.UPLOAD_PATH + "/");
    }

}

功能测试

使用Apifox软件发起一个Post请求,带上要上传的图片

在这里插入图片描述

通过链接访问图片,测试通过

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HCIA笔记6--路由基础

0. 概念 自治系统&#xff1a;一个统一管理的大型网络&#xff0c;由路由器组成的集合。 路由器隔离广播域&#xff0c;交换机隔离冲突域。 1.路由器工作原理 路由器根据路由表进行转发数据包&#xff1b; 路由表中没有路由&#xff0c;直接丢弃该数据包路由表中只有一条路…

Gentoo Linux部署LNMP

一、安装nginx 1.gentoo-chxf ~ # emerge -av nginx 提示配置文件需更新 2.gentoo-chxf ~ # etc-update 3.gentoo-chxf ~ # emerge -av nginx 4.查看并启动nginx gentoo-chxf ~ # systemctl status nginx gentoo-chxf ~ # systemctl start nginx gentoo-chxf ~ # syst…

DNS欺骗与钓鱼网站

本章节主要利用中间人攻击&#xff0c;DNS欺骗&#xff0c;实现被攻击者访问钓鱼网站&#xff0c;并实现收集被攻击者的登录用户名与密码。 链接&#xff1a;https://pan.baidu.com/s/1Os4HY-8BNWbVjpgQ1Bw-4Q 提取码&#xff1a;2lab 一、实训内容&#xff1a;DNS欺骗、AR…

【C++】数据类型与操作实践:详细解析与优化

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目一&#xff1a;三个数的反序输出1.1 题目描述与代码实现代码实现&#xff1a; 1.2 代码解析与细节说明1.3 使用 int 类型的合理性分析1.4 其他数据类型的考虑1.5 代码优…

python图像彩色数字化

效果展示&#xff1a; 目录结构&#xff1a; alphabets.py GENERAL {"simple": "%#*-:. ","complex": "$B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_~<>i!lI;:,\"^. " } # Full list could be found here…

【Linux】TCP网络编程

目录 V1_Echo_Server V2_Echo_Server多进程版本 V3_Echo_Server多线程版本 V3-1_多线程远程命令执行 V4_Echo_Server线程池版本 V1_Echo_Server TcpServer的上层调用如下&#xff0c;和UdpServer几乎一样&#xff1a; 而在InitServer中&#xff0c;大部分也和UDP那里一样&…

如何选择适合的网站关键词?

在做谷歌SEO时&#xff0c;选对关键词是成功的一半。很多人以为找到搜索量大的词就可以了&#xff0c;但实际上&#xff0c;关键词选择并不只是看流量高低&#xff0c;更重要的是与你的用户需求是否匹配。要想精准定位用户&#xff0c;首先需要了解你的目标受众是谁&#xff0c…

AI-agent矩阵营销:让品牌传播无处不在

矩阵营销是一种通过多平台联动构建品牌影响力的策略&#xff0c;而 AI-agent 技术让这一策略变得更加智能化。AI社媒引流王凭借其矩阵管理功能&#xff0c;帮助品牌在多个平台上实现深度覆盖与精准传播。 1. 矩阵营销的优势 品牌触达更广&#xff1a;多平台联动可以覆盖不同用…

Vue教程|搭建vue项目|Vue-CLI新版脚手架

一、安装Node环境 安装Node及Npm环境 Node下载地址:Node.js — Run JavaScript EverywhereNode.js is a JavaScript runtime built on Chromes V8 JavaScript engine.https://nodejs.org/en/ 安装完成后,检查安装是否成功,并检查版本,命令如下: node -v npm -v mac@Macd…

简单好用的折线图绘制!

折线图的概念及作用&#xff1a; 折线图&#xff08;Line Chart&#xff09;是一种常见的图表类型&#xff0c;用于展示数据的变化趋势或时间序列数据。它通过一系列的数据点&#xff08;通常表示为坐标系中的点&#xff09;与这些点之间的线段相连&#xff0c;直观地展示变量…

模型 布鲁姆法则

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。分层提升思维力。 1 布鲁姆法则的应用 1.1 布鲁姆法则在产品开发流程中的应用 背景&#xff1a; 在产品开发领域&#xff0c;创新和效率是关键。布鲁姆法则可以帮助产品经理和设计师系统地提升产品开…

如何通过实验室Lims系统解决效率和数据管理难题?

您的实验室是否还在为这些问题而苦恼呢&#xff1f; 提升企业业务效率&#xff1f; 质量可追溯、数据合规性&#xff1f; 提升客户服务质量&#xff1f; 如何让管理经验和检测数据有效积累? 实验室Lims系统功能 1、业务管理 2、检验管理 3、财务管理 4、客户管理 5、…

【目标跟踪】Anti-UAV数据集详细介绍

Anti-UAV数据集是在2021年公开的专用于无人机跟踪的数据集&#xff0c;该数据集采用RGB-T图像对的形式来克服单个类型视频的缺点&#xff0c;包含了318个视频对&#xff0c;并提出了相应的评估标准&#xff08;the state accurancy, SA)。 文章链接&#xff1a;https://arxiv.…

PyG教程:MessagePassing基类

PyG教程&#xff1a;MessagePassing基类 一、引言二、如何自定义消息传递网络1.构造函数2.propagate函数3.message函数4.aggregate函数5.update函数 三、代码实战1.图数据定义2.实现GNN的消息传递过程3.完整代码4.完整代码的精简版本 四、总结1.MessagePassing各个函数的执行顺…

Win10 系统下使用研华XNavi安装板卡驱动失败

配置&#xff1a;主板 AIMB-705G2&#xff0c;CPU i5-6500&#xff0c;系统 Windows10_64bit_Pro_22H2&#xff0c; 测试&#xff1a; 1、多次安装驱动。FAIL 2、尝试在其他电脑上移植板卡驱动并且使用数字签名安装。FAIL 3、系统更新到WIN10最新版本。FAIL 4、杀毒软件卸…

用三维模型的顶点法向量计算法线贴图

法线贴图的核心概念是在不增加额外多边形数目的情况下&#xff0c;通过模拟细节来改善光照效果。具体流程包括&#xff1a; 法线的计算与存储&#xff1a;通过法线映射将三维法线向量转化为法线贴图的 RGB 值。渲染中的使用&#xff1a;在片段着色器中使用法线贴图来替代原有的…

idea编译与maven编译的问题

先说下idea编译按钮的位置 编译运行时&#xff0c;会在idea底部出现Build面板 比较&#xff1a; idea编译器编译整个项目 maven编译器根据pom.xml的配置&#xff0c;可实现灵活编译 两套编译会遇到的问题&#xff1a; maven 编译成功 &#xff0c;但idea编译失败&#xff…

deepin 安装 chrome 浏览器

deepin 安装 chrome 浏览器 最近好多小伙伴儿和我说 deepin 无法安装最新的谷歌浏览器 其实是因为最新的 谷歌浏览器 其中的一个依赖需要提前安装 提前安装依赖然后再安装谷歌浏览器就可以了 安装 fonts-liberationsudo apt -y install fonts-liberation安装 chrome 浏览器sudo…

《String类》

目录 一、定义与概述 二、创建字符串对象 2.1 直接赋值 2.2 使用构造函数 三、字符串的不可变性 四、常用方法 4.1 String对象的比较 4.1.1 比较是否引用同一个对象 4.1.2 boolean equals(Object anObject)方法&#xff1a;按照字典序比较 4.1.3 int compareTo(Strin…

OpenSSH-9.9p1 OpenSSL-3.4.0 升级步骤详细

前言 收到漏洞扫描通知 OpenSSH 安全漏洞(CVE-2023-38408) OpenSSH 安全漏洞(CVE-2023-51385) OpenSSH 安全漏洞(CVE-2023-51384) OpenSSH 安全漏洞(CVE-2023-51767) OpenSSH 安全漏洞(CVE-2023-48795) OpenSSH&#xff08;OpenBSD SecureShell&#xff09;是加拿大OpenBSD计划…