【OSS对象存储】Springboot集成阿里云OSS + 私有化部署Minio

【OSS对象存储】Springboot集成阿里云OSS + 私有化部署Minio

  • 一、摘要
  • 二、POM依赖
  • 三、配置文件
  • 四、表结构设计
  • 五、代码实现
    • 5.1 代码包结构
    • 5.2 API封装
    • 5.3 增删改查
  • 六、扩展
    • 6.1 Minio配置https访问

一、摘要

  1. 掌握阿里云OSS、私有化部署Minio两种对象存储的使用方式
  2. 运用工厂+策略模式,封装OSS对象存储API,可实现动态切换
  3. Docker部署Minio

二、POM依赖

<!-- 阿里云对象存储 -->
<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version>
</dependency><!-- Minio私有化对象存储 -->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.6</version>
</dependency>

三、配置文件

ai-wxapp:oss:#  strategy: ALI_OSS#  endpoint: oss-cn-beijing.aliyuncs.com#  accessKeyId: xxxxxx#  accessKeySecret: xxxxxx#  bucketName: xxxxstrategy: MinIOendpoint: http://localhost:9000accessKeyId: xxxxxxaccessKeySecret: xxxxxxbucketName: xxxx

OssConfig.java:

@Data
@Component
@RefreshScope
public class OssConfig {@Value("${ai-wxapp.oss.strategy}")private String ossStrategy;@Value("${ai-wxapp.oss.bucketName}")private String bucketName;@Value("${ai-wxapp.oss.endpoint}")private String endpoint;@Value("${ai-wxapp.oss.accessKeyId}")private String accessKeyId;@Value("${ai-wxapp.oss.accessKeySecret}")private String accessKeySecret;
}

四、表结构设计

表结构数据

CREATE TABLE `oss_file` (`id` bigint NOT NULL AUTO_INCREMENT,`file_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件id',`original_file_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '原始文件名',`file_ext` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '文件扩展名',`file_path` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '文件路径',`create_by` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '创建人',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_by` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '修改人',`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`),UNIQUE KEY `oss_file_unique` (`file_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='对象存储信息表';

五、代码实现

5.1 代码包结构

在这里插入图片描述

5.2 API封装

ObjectStorageEnum.java:
```java
/*** @ClassName ObjectStorageEnum* @Description 对象存储策略枚举* @Author shn* @Date 2023/11/2 18:17* @Version 1.0**/
@Getter
public enum ObjectStorageEnum {/*** 阿里云OSS对象存储*/ALI_OSS,/*** MinIO私有化对象存储*/MinIO
}

IOssFileRepository.java

public interface IOssFileRepository {OssFileEntity getByFileId(String fileId);List<OssFileEntity> getByFileIds(List<String> fileIds);void saveEntity(OssFileEntity build);
}

ObjectStorageFactory.java

/*** @ClassName ObjectStorageFactory* @Description 对象存储策略工厂* @Author shn* @Date 2023/11/2 18:23* @Version 1.0**/
@Component
public class ObjectStorageFactory implements ApplicationContextAware, InitializingBean {private ApplicationContext applicationContext;private static final Map<ObjectStorageEnum, ObjectStorage> SERVICES = new HashMap<>();/*** 根据枚举获取策略实现类实例*/public ObjectStorage getInstance(ObjectStorageEnum storageEnum) {return SERVICES.get(storageEnum);}@Overridepublic void afterPropertiesSet() {Map<String, ObjectStorage> beans = applicationContext.getBeansOfType(ObjectStorage.class);for (ObjectStorage bean : beans.values()) {SERVICES.put(bean.getStrategyEnum(), bean);}}@Overridepublic void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}

ObjectStorage.java:

/*** @ClassName ObjectStorage* @Description 对象存储接口,异常全部对外抛出* @Author shn* @Date 2023/11/2 17:51* @Version 1.0**/
public interface ObjectStorage {/*** 获取策略枚举*/ObjectStorageEnum getStrategyEnum();/*** 判断对象是否存在** @param bucketName 桶名称* @param objectName 对象存储全路径,不包含桶名称*/boolean objectExists(String bucketName, String objectName) throws Exception;/*** 上传对象** @param bucketName 桶名称* @param objectName 对象存储全路径,不包含桶名称* @param in         输入流*/void uploadObject(String bucketName, String objectName, InputStream in) throws Exception;/*** 下载对象** @param bucketName 桶名称* @param objectName 对象存储全路径,不包含桶名称*/byte[] downloadObject(String bucketName, String objectName) throws Exception;/*** 删除对象** @param bucketName 桶名称* @param objectName 对象存储全路径,不包含桶名称*/void deleteObject(String bucketName, String objectName) throws Exception;/*** 删除对象** @param bucketName  桶名称* @param objectNames 对象存储全路径,不包含桶名称*/void deleteObjects(String bucketName, List<String> objectNames);/*** 判断桶是否存在** @param bucketName 桶名称*/boolean bucketExists(String bucketName) throws Exception;/*** 创建桶** @param bucketName 桶名称*/void createBucket(String bucketName) throws Exception;/*** 删除桶** @param bucketName 桶名称*/void deleteBucket(String bucketName) throws Exception;/*** 列出桶中的对象** @param bucketName 桶名称*/List<String> listObjects(String bucketName) throws Exception;/*** 获取临时授权访问url** @param bucketName    桶名称* @param objectName    对象存储全路径,不包含桶名称* @param expireSeconds 过期时间(单位:秒)* @return java.lang.String* @author shn 2023/11/8 13:27*/String getTempAccessUrl(String bucketName, String objectName, int expireSeconds);
}

AliOssService.java

/*** @ClassName AliOssService* @Description 阿里云OSS对象存储* @Author shn* @Date 2023/11/2 18:10* @Version 1.0**/
@Slf4j
@Service("aliOssService")
public class AliOssService implements ObjectStorage {@Resourceprivate OssConfig ossConfig;/*** 获取ossClient <br>* 1)每次请求都创建一个新的ossClient <br>* 2)虽然ossClient是线程安全的,但是考虑到OssConfig支持动态刷新,所以这里没有设计为单例。<br>* 3)因此ossClient需要手动关闭* 参考:<a href="https://help.aliyun.com/zh/oss/developer-reference/initialization-3">...</a> <br>*/private OSS getOssClient() {return new OSSClientBuilder().build(ossConfig.getEndpoint(),ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());}/*** 关闭ossClient*/private void close(OSS ossClient) {if (ossClient != null) {ossClient.shutdown();}}@Overridepublic ObjectStorageEnum getStrategyEnum() {return ObjectStorageEnum.ALI_OSS;}@Overridepublic boolean objectExists(String bucketName, String objectName) {OSS ossClient = getOssClient();try {boolean bucketExist = ossClient.doesBucketExist(bucketName);if (!bucketExist) {return false;}boolean exists = ossClient.doesObjectExist(bucketName, objectName);if (!exists) {return false;}} finally {close(ossClient);}return true;}@Overridepublic void uploadObject(String bucketName, String objectName, InputStream in) throws Exception {if (in.available() <= 0) {throw new IOException("输入流为空");}OSS ossClient = getOssClient();try {boolean bucketExist = ossClient.doesBucketExist(bucketName);if (!bucketExist) {throw new ServiceException("bucket不存在");}ossClient.putObject(bucketName, objectName, in);log.info("文件上传OSS成功,文件路径: {}", objectName);} finally {close(ossClient);in.close();}}@Overridepublic byte[] downloadObject(String bucketName, String objectName) {OSS ossClient = getOssClient();try {boolean exists = ossClient.doesObjectExist(bucketName, objectName);if (!exists) {throw new ServiceException("文件不存在");}// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。OSSObject ossObject = ossClient.getObject(bucketName, objectName);InputStream in = ossObject.getObjectContent();// 该API默认会关闭流return IoUtil.readBytes(in);} finally {close(ossClient);}}@Overridepublic void deleteObject(String bucketName, String objectName) {OSS ossClient = getOssClient();try {// 删除文件或目录。如果要删除目录,目录必须为空。ossClient.deleteObject(bucketName, objectName);} finally {close(ossClient);}}@Overridepublic void deleteObjects(String bucketName, List<String> objectNames) {OSS ossClient = getOssClient();try {DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName);request.setKeys(objectNames);// 静默删除,删除操作不返回删除结果信息,效率高,需结合业务场景选择是否开启request.setQuiet(true);ossClient.deleteObjects(request);} finally {close(ossClient);}}@Overridepublic boolean bucketExists(String bucketName) {OSS ossClient = getOssClient();try {// 判断存储空间是否存在boolean exists = ossClient.doesBucketExist(bucketName);if (!exists) {return false;}} finally {close(ossClient);}return true;}@Overridepublic void createBucket(String bucketName) {OSS ossClient = getOssClient();try {// 创建CreateBucketRequest对象。CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);// 如果创建存储空间的同时需要指定存储类型、存储空间的读写权限、数据容灾类型, 请参考如下代码。// 此处以设置存储空间的存储类型为标准存储为例介绍。//createBucketRequest.setStorageClass(StorageClass.Standard);// 数据容灾类型默认为本地冗余存储,即DataRedundancyType.LRS。如果需要设置数据容灾类型为同城冗余存储,请设置为DataRedundancyType.ZRS。//createBucketRequest.setDataRedundancyType(DataRedundancyType.ZRS);// 设置存储空间读写权限为公共读,默认为私有。//createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);// 在支持资源组的地域创建Bucket时,您可以为Bucket配置资源组。//createBucketRequest.setResourceGroupId(rsId);// 创建存储空间ossClient.createBucket(createBucketRequest);} finally {close(ossClient);}}@Overridepublic void deleteBucket(String bucketName) {OSS ossClient = getOssClient();try {// 删除存储空间ossClient.deleteBucket(bucketName);} finally {close(ossClient);}}@Overridepublic List<String> listObjects(String bucketName) {return null;}@Overridepublic String getTempAccessUrl(String bucketName, String objectName, int expireSeconds) {OSS ossClient = getOssClient();try {boolean exists = ossClient.doesObjectExist(bucketName, objectName);if (!exists) {throw new ServiceException("文件不存在");}String url = ossClient.generatePresignedUrl(bucketName, objectName, DateUtil.offsetSecond(new Date(), expireSeconds)).toString();return StringUtil.replaceHttp2Https(url);} finally {close(ossClient);}}
}

MinIoService.java:

@Slf4j
@Service("minIoService")
public class MinIoService implements ObjectStorage {@Resourceprivate OssConfig ossConfig;/*** 获取 MinioClient,每次请求都创建一个新的 MinioClient*/private MinioClient getMinioClient() {return MinioClient.builder().endpoint(ossConfig.getEndpoint()).credentials(ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret()).build();}@Overridepublic ObjectStorageEnum getStrategyEnum() {return ObjectStorageEnum.MinIO;}@SneakyThrows(Exception.class)@Overridepublic boolean objectExists(String bucketName, String objectName) {return false;}@SneakyThrows(Exception.class)@Overridepublic void uploadObject(String bucketName, String objectName, InputStream in) {if (in.available() <= 0) {throw new IOException("输入流为空");}try {getMinioClient().putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(in, in.available(), -1).build());log.info("文件上传Minio成功,文件路径: {}", objectName);} finally {in.close();}}@SneakyThrows(Exception.class)@Overridepublic byte[] downloadObject(String bucketName, String objectName) {InputStream in = getMinioClient().getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());// 该API默认会关闭流return IoUtil.readBytes(in);}@SneakyThrows(Exception.class)@Overridepublic void deleteObject(String bucketName, String objectName) {getMinioClient().removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());}@SneakyThrows(Exception.class)@Overridepublic void deleteObjects(String bucketName, List<String> objectNames) {if (CollectionUtil.isEmpty(objectNames)) {return;}getMinioClient().removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objectNames.stream().map(DeleteObject::new).collect(Collectors.toList())).build());}@SneakyThrows(Exception.class)@Overridepublic boolean bucketExists(String bucketName) {return getMinioClient().bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}@SneakyThrows(Exception.class)@Overridepublic void createBucket(String bucketName) {MinioClient minioClient = getMinioClient();boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (!exist) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}@SneakyThrows(Exception.class)@Overridepublic void deleteBucket(String bucketName) {getMinioClient().removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}@SneakyThrows(Exception.class)@Overridepublic List<String> listObjects(String bucketName) {return null;}@SneakyThrows(Exception.class)@Overridepublic String getTempAccessUrl(String bucketName, String objectName, int expireSeconds) {String url = getMinioClient().getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).expiry(expireSeconds).build());log.info("Minio origin url: {}", url);return url;}
}

5.3 增删改查

OssFileEntity.java:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class OssFileEntity {private String fileId;private String originalFileName;private String fileExt;private String filePath;
}
IOssFileService.java
```java
public interface IOssFileService {OssFileEntity getByFileId(String fileId);byte[] downloadFile(String fileId) throws Exception;void deleteFiles(List<String> fileIds);String uploadFile(byte[] fileBytes, String originalFileName);String getTempAccessUrl(String fileId, int expireSeconds);}

OssFileService.java

@Slf4j
@Service
public class OssFileService implements IOssFileService {@Resourceprivate OssConfig ossConfig;@Resourceprivate ObjectStorageFactory objectStorageFactory;@Resourceprivate RedisUtils redisUtils;@Resourceprivate IOssFileRepository ossFileRepository;private ObjectStorage getObjectStorage() {return objectStorageFactory.getInstance(ObjectStorageEnum.valueOf(ossConfig.getOssStrategy()));}public OssFileEntity getByFileId(String fileId) {OssFileEntity entity = ossFileRepository.getByFileId(fileId);if (entity == null) {throw new ServiceException(CommonError.OSS_FILE_NOT_EXIST);}return entity;}public byte[] downloadFile(String fileId) throws Exception {OssFileEntity entity = getByFileId(fileId);return getObjectStorage().downloadObject(ossConfig.getBucketName(), entity.getFilePath());}public void deleteFiles(List<String> fileIds) {List<OssFileEntity> list = ossFileRepository.getByFileIds(fileIds);if (CollectionUtil.isEmpty(list)) {return;}getObjectStorage().deleteObjects(ossConfig.getBucketName(),list.stream().map(OssFileEntity::getFilePath).collect(Collectors.toList()));}/*** 文件上传oss** @param fileBytes        文件* @param originalFileName 文件原始名称* @return java.lang.String 文件id* @author shn 2023/12/13 11:42*/public String uploadFile(byte[] fileBytes, String originalFileName) {if (ArrayUtil.isEmpty(fileBytes)|| StringUtils.isBlank(originalFileName)) {throw new ServiceException(CommonError.OSS_FILE_UPLOAD_ERROR);}// 上传ossString fileId = this.generateFileId();String fileExtension = FileUtil.getFileExtension(originalFileName);String filePath = buildFilePath(fileId, fileExtension);try {InputStream inputStream = new ByteArrayInputStream(fileBytes);getObjectStorage().uploadObject(ossConfig.getBucketName(), filePath, inputStream);} catch (Exception e) {log.error("文档上传失败", e);throw new ServiceException(CommonError.OSS_FILE_UPLOAD_ERROR);}// 保存数据库ossFileRepository.saveEntity(OssFileEntity.builder().fileId(fileId).originalFileName(originalFileName).fileExt(fileExtension).filePath(filePath).build());return fileId;}/*** 获取临时url** @param fileId        文件id* @param expireSeconds 过期时间(秒)* @return java.lang.String* @author shn 2024/05/29 15:36*/public String getTempAccessUrl(String fileId, int expireSeconds) {OssFileEntity entity = getByFileId(fileId);// 先查缓存String key = String.format(RedisKeys.OSS_FILE_URL_KEY, fileId);if (redisUtils.hasKey(key) && redisUtils.getExpire(key) > 0L) {return String.valueOf(redisUtils.get(key));}// 缓存不存在,则从OSS获取临时URL。设置OSS过期时间比 Redis晚2秒,保证 URL一直可用。String url = getObjectStorage().getTempAccessUrl(ossConfig.getBucketName(), entity.getFilePath(), expireSeconds + 2);// 保存临时URL到RedisredisUtils.set(key, url, expireSeconds, TimeUnit.SECONDS);return url;}/*** 构建文件存储路径*/@NotNullprivate static String buildFilePath(String fileId, String fileExtension) {return DateUtil.today() + "/" + fileId + fileExtension;}/*** 生成文件id*/private String generateFileId() {String fileId = "file_" + IdUtil.simpleUUID();log.info("generate new file id: {}", fileId);return fileId;}
}

六、扩展

6.1 Minio配置https访问

目标:实现https域名访问minio控制台、资源
步骤:
1)前往阿里云下载Apache证书文件
在这里插入图片描述
2)修改公钥和私钥文件名为private.key、public.crt 并上传至服务端
在这里插入图片描述
3)Nginx配置

upstream minio_s3 {#least_conn;server 127.0.0.1:9000;
}upstream minio_console {#least_conn;server 127.0.0.1:9001;
}server {listen 80;server_name huiling.xxxx.com;listen 443 ssl;server_name huiling.xxxx.com;# SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则# error_page 404/404.html;ssl_certificate     /etc/nginx/ssl/huiling/huiling.xxxx.com.pem;  # pem文件的路径ssl_certificate_key  /etc/nginx/ssl/huiling/huiling.xxxx.com.key; # key文件的路径ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;ssl_prefer_server_ciphers on;ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;# error_page 497  https://$host$request_uri;proxy_buffering off;proxy_request_buffering off;location / {client_max_body_size 10m;proxy_set_header Host $http_host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;proxy_connect_timeout 300;# Default is HTTP/1, keepalive is only enabled in HTTP/1.1proxy_http_version 1.1;proxy_set_header Connection "";chunked_transfer_encoding off;proxy_pass https://minio_s3; # This uses the upstream directive definition to load balance}location /minio/ui/ {rewrite ^/minio/ui/(.*) /$1 break;proxy_set_header Host $http_host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-NginX-Proxy true;# This is necessary to pass the correct IP to be hashedreal_ip_header X-Real-IP;proxy_connect_timeout 300;# To support websockets in MinIO versions released after January 2023proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";# Some environments may encounter CORS errors (Kubernetes + Nginx Ingress)# Uncomment the following line to set the Origin request to an empty string# proxy_set_header Origin '';chunked_transfer_encoding off;proxy_pass https://minio_console; # This uses the upstream directive definition to load balance}
}

4)Docker启动配置

# 定义compose语义版本
version: '3.8'
# 定义服务
services:minio:image: minio/minio:latestcontainer_name: miniorestart: unless-stoppedcommand: server /data --console-address ":9001" -address ":9000"environment:TZ: Asia/ShanghaiLANG: en_US.UTF-8MINIO_SERVER_URL: https://huiling.xxx.comMINIO_BROWSER_REDIRECT_URL: https://huiling.xxx.com/minio/uiMINIO_ROOT_USER: minioadminMINIO_ROOT_PASSWORD: minioadmin9339volumes:- "/usr/local/docker/minio/data:/data"- "/usr/local/docker/minio/config:/root/.minio"ports:- "9000:9000"- "9001:9001"

5)验证
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++指南】内存管理(上)

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注

vue上传Excel文件并直接点击文件列表进行预览

本文主要内容&#xff1a;用elementui的Upload 组件上传Excel文件&#xff0c;上传后的列表采用xlsx插件实现点击预览表格内容效果。 在项目中可能会有这样的需求&#xff0c;有很多种方法实现。但是不想要跳转外部地址&#xff0c;所以用了xlsx插件来解析表格&#xff0c;并展…

总结一些vue3小知识3

总结一些vue3小知识1&#xff1a;http://t.csdnimg.cn/C5vER 总结一些vue3小知识2&#xff1a;http://t.csdnimg.cn/sscid 1.限制时间选择器只能选择后面的日期 说明&#xff1a;disabled-date属性是一个用来判断该日期是否被禁用的函数&#xff0c;接受一个 Date 对象作为参…

科普文:分布式架构中的三高:高并发、高性能、高可用

关于高并发 高并发场景 互联网应用以及云计算的普及&#xff0c;使得架构设计和软件技术的关注点从如何实现复杂的业务逻 辑&#xff0c;转变为如何满足大量用户的高并发访问请求。 一个简单的计算处理过程&#xff0c;如果一旦面对大量的用户访问&#xff0c;整个技术挑战就…

DP 整数拆分不同的二叉搜索树 DAY21

整数拆分&#xff1f; 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。示例 2: 输入: n 10 输…

实验2-2-1 温度转换

#include<stdio.h> #include <math.h> int main(){int c,f150;c5*(f-32)/9;printf("fahr 150, celsius %d",c); }

sqlilabs解题方法

Lass1 查询id为1的用户名和密码 查询id为2的用户名和密码 没有回显&#xff0c;不含id-1的行 判断字段数&#xff0c;字段数为3 查询数据库用户名&#xff0c;和数据库名 查询时id必须超出数据库以外&#xff0c;一般用-1 用户名&#xff1a;user() 数据库名&#xff1a;databa…

redis:清除缓存的最简单命令示例

清除redis缓存命令(执行命令列表见截图) 1.打开cmd窗口&#xff0c;并cd进入redis所在目录 2.登录redis redis-cli 3.查询指定队列当前的记录数 llen 队列名称 4.清除指定队列所有记录 ltrim 队列名称 1 0 5.再次查询&#xff0c;确认队列的记录数是否已清除

opencascade AIS_Manipulator源码学习

前言 AIS_Manipulator 是 OpenCASCADE 库中的一个类&#xff0c;用于在3D空间中对其他交互对象或一组对象进行局部变换。该类提供了直观的操控方式&#xff0c;使用户可以通过鼠标进行平移、缩放和旋转等操作。 详细功能 交互对象类&#xff0c;通过鼠标操控另一个交互对象…

【Django】 读取excel文件并在前端以网页形式显示-安装使用Pandas

文章目录 安装pandas写views写urls安装openpyxl重新调试 安装pandas Pandas是一个基于NumPy的Python数据分析库&#xff0c;可以从各种文件格式如CSV、JSON、SQL、Excel等导入数据&#xff0c;并支持多种数据运算操作&#xff0c;如归并、再成形、选择等。 更换pip源 pip co…

在 Postman 中设置全局 token

目录 问题描述解决方案 问题描述 在使用 Postman 进行接口测试时&#xff0c;经常会遇到在 Header 中添加 token 的情况。当接口数量较多时&#xff0c;需要为每个接口进行设置&#xff0c;而且当 token 失效时需要重新获取并设置&#xff0c;这样一来效率较低。 解决方案 下…

MSPM0G3507学习笔记1:开发环境_引脚认识与点灯

今日速通一款Ti的单片机用于电赛&#xff1a;MSPM0G3507 这里默认已经安装好了Keil5_MDK 首先声明一下: 因为是速成&#xff0c;所以需要一定单片机学习基础&#xff0c;然后我写的也不会详细&#xff0c;这个专栏的笔记也就是自己能看懂就行的目标~~~ 文章提供测试代码解…

Matlab进阶绘图第65期—带分组折线段的柱状图

带分组折线段的柱状图是在原始柱状图的基础上&#xff0c;在每组柱状图位置处分别添加折线段&#xff0c;以进行对比或添加额外信息。 由于Matlab中未收录带分组折线段的柱状图的绘制函数&#xff0c;因此需要大家自行设法解决。 本文使用自制的BarwithGroupedLine小工具进行…

「Ant Design」Antd 中卡片如何完全不展示内容区域、按需展示内容区域、不展示标题

前言 下面是默认的 Antd 卡片&#xff0c;由以下区域组成 处理 Antd 的 Card 展示形式大致有下面三种 卡片完全不展示内容区域 const App () > (<Card title"Default size card" extra{<a href"#">More</a>} style{{ width: 300 }}b…

SSRF过滤攻击

SSRF绕过&#xff1a; 靶场地址&#xff1a;重庆橙子科技SSRF靶场 这个是毫无过滤的直接读取&#xff0c;但是一般网站会设置有对SSRF的过滤&#xff0c;比如将IP地址过滤。 下面是常用的绕过方式&#xff1a; 1.环回地址绕过 http://127.0.0.1/flag.php http://017700…

C++11新特性——智能指针——参考bibi《 原子之音》的视频以及ChatGpt

智能指针 一、内存泄露1.1 内存泄露常见原因1.2 如何避免内存泄露 二、实例Demo2.1 文件结构2.2 Dog.h2.3 Dog.cpp2.3 mian.cpp 三、独占式智能指针:unique _ptr3.1 创建方式3.1.1 ⭐从原始(裸)指针转换&#xff1a;3.1.2 ⭐⭐使用 new 关键字直接创建&#xff1a;3.1.3 ⭐⭐⭐…

如何录制电脑内部声音?全方位介绍电脑录音软件:8款在线录音!(2024重新整理)

如何录制电脑内部声音&#xff1f;不管是娱乐圈还是现实生活&#xff0c;【录音】这个功能的重要性不言而喻。而电脑录音已在影视配音、音视频剪辑、会议记录、在线教育等多个领域发光发热&#xff01; 本文将为您推荐8款电脑录音软件&#xff0c;并详细介绍电脑录音的多种方式…

electron 网页TodoList工具打包成win桌面应用exe

参考&#xff1a; electron安装&#xff08;支持win、mac、linux桌面应用&#xff09; https://blog.csdn.net/weixin_42357472/article/details/140643624 TodoList工具 https://blog.csdn.net/weixin_42357472/article/details/140618446 electron打包过程&#xff1a; 要将…

51单片机-第五节-串口通信

1.什么是串口&#xff1f; 串口是通讯接口&#xff0c;实现两个设备的互相通信。 单片机自带UART&#xff0c;其中引脚有TXD发送端&#xff0c;RXD接收端。且电平标准为TTL&#xff08;5V为1,0V为0&#xff09;。 2.常见电平标准&#xff1a; &#xff08;1&#xff09;TTL电…

景区AR导航营销系统:技术解决方案与实施效益分析

随着旅游市场的竞争日益激烈&#xff0c;景区需要不断创新以吸引游客。景区 AR 导航将虚拟画面与现实场景相结合&#xff0c;为游客提供了更加直观、生动的导航服务。对于景区而言&#xff0c;这一创新技术无疑是吸引游客目光、提升景区知名度的有力武器。通过独特的 AR 导航体…