SpringBoot整合阿里云OSS、天翼云OSS、MinIO对象存储

news/2024/4/20 13:37:26/文章来源:https://blog.csdn.net/weixin_33005117/article/details/133858006

大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

以下是正文!

对象存储是什么?

对象存储是一种数据存储方式,它将数据分割成不同的对象,并为每个对象分配一个唯一的标识符,用于访问和操作数据。这些对象被存储在多个服务器上,以确保数据的可靠性和可用性。对象存储适合存储大量数据,具有可扩展性、低成本和高安全性等特点。

这段话太专业了,以至于有点让人看不懂。私以为对象存储就是『分布式文件存储系统』,可能是我们只会用它来存储图片、视频、文档等文件吧😅,然后数据库(比如MySQL)只存储文件的访问链接。目前接触的对象存储有
阿里云对象存储OSS、天翼云对象存储融合版、自建对象存储MinIO。

  • 阿里云对象存储OSS

帮助文档链接

  • 天翼云对象存储融合版

帮助文档链接

  • MinIO对象存储

帮助文档链接

这三款对象存储产品对比如下

产品功能特点是否收费是否开源对接难易度
阿里云对象存储OSS阿里云OSS提供了丰富的存储、数据处理和分发功能,可以满足各种场景的需求收费不开源只需要ak/sk,然后看文档即可
天翼云对象存储融合版天翼云对象存储融合版主要面向移动互联网应用,提供了数据管理、在线处理等功能收费不开源只需要ak/sk,然后看文档即可
MinIO对象存储MinIO专注于提供高性能、高可用的对象存储服务。免费开源只需要ak/sk,然后看文档即可

总之有钱的话就买服务,没钱就自己搭,总有合适自己的😉。

对象存储和数据库的区别

各维度对比

存储数据结构数据处理存储方式可伸缩性
数据库存储数据库是基于表格的存储方式,每个表格有特定的列和行。数据库主要用于存储结构化数据,如文本、数字和日期等。数据库可以进行更复杂的数据处理,如查询、过滤和排序等。数据库通常使用关系型数据库或NoSQL数据库等数据库在扩展性上需要更多的运维和管理
对象存储对象存储是基于对象的存储方式,每个对象可以是任何类型的文件对象存储通常用于存储大量非结构化数据,如图片、视频和音频等对象存储通常使用分布式存储技术将数据分散存储在不同的节点上对象存储具有良好的扩展性,因此可以轻松地添加新的节点来处理更多的数据

项目对接流程图对比

CRUD之阿里云对象存储

1. 安装Java SDK

一般都是通过maven直接引入

<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version>
</dependency>

2. 获取Client

官方获取Client代码示例

// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
String endpoint = "yourEndpoint";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "yourAccessKeyId";
String accessKeySecret = "yourAccessKeySecret";// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);// 关闭OSSClient。
ossClient.shutdown();                    

我一般会将其变成一个Component

@Component
public class AliyunOssClient {@Value("${oss.endpoint}")private String endpoint;@Value("${aliyun.accessKeyId}")private String accessKeyId;@Value("${aliyun.accessKeySecret}")private String accessKeySecret;@Bean(name = "aliyunOssClient")public OSS aliyunOssClient() {// 构建并返回OSSClientreturn new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);}
}

3. 增删改查操作

(1)对象公共读&上传和访问

官方文档

 /*** 简单上传-流式上传-公共读** @param bucketName  bucket名称* @param key         文件名* @param inputStream 输入流* @return PutObjectResult 上传结果*/
public static PutObjectResult putObjectByInputStreamAndPublicRead(String bucketName, String key,InputStream inputStream) {ObjectMetadata metadata = new ObjectMetadata();//设置StorageClass为Standard即为metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());//设置读写为公共读写metadata.setObjectAcl(CannedAccessControlList.PublicRead);// 准备OSS上传对象请求PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream);putObjectRequest.setMetadata(metadata);// 上传return ossClient.putObject(putObjectRequest);
}

当设置文件的访问权限为公共读时,直接拼接文件的访问链接就可以了,比如endpoint为oss-cn-hangzhou.aliyuncs.com,bucket为file-bucket,key为/test/file/1.png
那么访问链接就为:https://file-bucket.oss-cn-hangzhou.aliyuncs.com/test/file/1.png

(2)对象私有读&上传和访问

官方文档

 /*** 简单上传-流式上传-私有读写** @param bucketName  bucket名称* @param key         文件名* @param inputStream 输入流* @return PutObjectResult 上传结果*/public static PutObjectResult putObjectByInputStreamAndPrivate(String bucketName, String key,InputStream inputStream) {ObjectMetadata metadata = new ObjectMetadata();//设置StorageClass为Standard即为metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());//设置读写为公共读写metadata.setObjectAcl(CannedAccessControlList.Private);// 准备OSS上传对象请求PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream);putObjectRequest.setMetadata(metadata);// 上传return ossClient.putObject(putObjectRequest);}

当设置文件的访问权限为私有读时,直接拼接访问链接是没法访问该文件的,访问会报如下错误:

<Error>
<Code>AccessDenied</Code>
<Message>You do not have read permission on this object.</Message>
<RequestId>6488223453BCC63831BB3EC4</RequestId>
<HostId>file-bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
<EC>0003-00000005</EC>
</Error>

这时就需要对key进行加签操作了,加签代码如下:

 /*** 通过bucketName、key、过期时间生成文件访问链接(时效性)** @param bucketName bucket名称* @param key        文件名* @param expiration 过期时间* @return 文件访问链接*/public static String getSignObjectUrl(String bucketName, String key, Date expiration) {return ossClient.generatePresignedUrl(bucketName, key, expiration).toString();}

那么获取的链接格式就是这样了:https://file-bucket.oss-cn-hangzhou.aliyuncs.com/test/file/test.png?Expires=xxx&OSSAccessKeyId=xxxx&Signature=xxxx

(3)对象下载

官方文档

下载到文件
   /*** 通过bucketName、key、文件路径 下载OSS文件到本地文件** @param bucketName bucket名称* @param key        文件名* @param pathName   本地文件路径*/public static void getObjectToFile(String bucketName, String key, String pathName) {// 下载OSS文件到本地ossClient.getObject(new GetObjectRequest(bucketName, key), new File(pathName));}
流式下载
 /*** 通过bucketName、key 下载OSS变成字节流** @param bucketName bucket名称* @param key        文件名*/public static void getObjectToStream(String bucketName, String key) {OSSObject ossObject = null;BufferedReader reader = null;try {ossObject = ossClient.getObject(bucketName, key);reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));while (true) {String line = null;line = reader.readLine();if (line == null) { break; }System.out.println("\n" + line);}} catch (IOException e) {log.error("下载oss文件异常", e);} finally {try {// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。reader.close();// ossObject对象使用完毕后必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。ossObject.close();} catch (IOException e) {log.error("关闭流发生异常", e);}}}

(4)对象删除

官方文档

   /*** 通过bucketName、key删除文件** @param bucketName bucket名称* @param key        文件名*/public static void delObject(String bucketName, String key) {// 删除文件或目录。如果要删除目录,目录必须为空。ossClient.deleteObject(bucketName, key);}

(5)图片处理

官方文档
这个接口目前只有阿里云有写文档,其他云都是一笔带过没有详细说明,作用是将上传到OSS的原始图片进行缩放、旋转、加水印等操作,非常好用,在此强烈安利一波!!!

  • 将图片缩放为固定宽高100 px
// 将图片缩放为固定宽高100 px。
String style = "image/resize,m_fixed,w_100,h_100";
GetObjectRequest request = new GetObjectRequest(bucketName, objectName);
request.setProcess(style);
// 将处理后的图片命名为example-resize.jpg并保存到本地。
// 填写本地文件的完整路径,例如D:\\localpath\\example-resize.jpg。如果指定的本地文件存在会覆盖,不存在则新建。
// 如果未指定本地路径只填写了本地文件名称(例如example-resize.jpg),则文件默认保存到示例程序所属项目对应本地路径中。
ossClient.getObject(request, new File("D:\\localpath\\example-resize.jpg"));
  • 从坐标(100,100)开始,将图片裁剪为宽高100 px
// 从坐标(100,100)开始,将图片裁剪为宽高100 px。
style = "image/crop,w_100,h_100,x_100,y_100";
request = new GetObjectRequest(bucketName, objectName);
request.setProcess(style);
// 将处理后的图片命名为example-crop.jpg并保存到本地。
ossClient.getObject(request, new File("D:\\localpath\\example-crop.jpg"));
  • 将图片旋转90°
 // 将图片旋转90°。
style = "image/rotate,90";
request = new GetObjectRequest(bucketName, objectName);
request.setProcess(style);
// 将处理后的图片命名为example-rotate.jpg并保存到本地。
ossClient.getObject(request, new File("D:\\localpath\\example-rotate.jpg"));
  • 在图片中添加文字水印。
// 在图片中添加文字水印。
// 文字水印的文字内容经过Base64编码后,再将编码结果中的加号(+)替换成短划线(-),正斜线(/)替换成下划线(_)并去掉尾部的等号(=),从而得到水印字符串。
// 指定文字水印的文字内容为Hello World,文字内容进行编码处理后得到的水印字符串为SGVsbG8gV29ybGQ。
style = "image/watermark,text_SGVsbG8gV29ybGQ";
request = new GetObjectRequest(bucketName, objectName);
request.setProcess(style);
// 将处理后的图片命名为example-watermarktext.jpg并保存到本地。
ossClient.getObject(request, new File("D:\\localpath\\example-watermarktext.jpg"));
  • 在图片中添加图片水印。请确保水印图片已保存在图片所在Bucket中
// 在图片中添加图片水印。请确保水印图片已保存在图片所在Bucket中。
// 水印图片的完整路径经过Base64编码后,再将编码结果中的加号(+)替换成短划线(-),正斜线(/)替换成下划线(_)并去掉尾部的等号(=),从而得到水印字符串。
// 指定水印图片的完整路径为panda.jpg,完整路径进行编码处理后得到的水印字符串为cGFuZGEuanBn。
style = "image/watermark,image_cGFuZGEuanBn";
request = new GetObjectRequest(bucketName, objectName);
request.setProcess(style);
// 将处理后的图片命名为example-watermarkimage.jpg并保存到本地。
ossClient.getObject(request, new File("D:\\localpath\\example-watermarkimage.jpg"));

除了这些之外还有很多其他的功能,大家可以自己看文档~
顺便说一个我们使用的案例

我们之前在开发项目的时候,做了个文章发布的功能,简单来说就是PC后台管理端发布文章,然后在微信小程序、H5小程序点击查看。文章的内容是包括图文的,有些文章图片上传的是原图,一张好几兆大小,用户在手机端查看文章时加载慢不说,那流量更是跑得飞起。我们就是用这个功能对图片进行等比例缩放,控制图片大小来解决的。

当时我们使用富文本框写的文章,这种文章会把图片、文字和dom元素混在一起,而且每张图片大小、分辨率都不同,这种情况下后端是没法对图片进行处理的,能处理图片的只有前端。为了解决这个问题,阿里云OSS提供了一个参数:x-oss-process。具体效果,我们直接看对比图:

从上图看,加了参数后,图片直接变小,但也模糊了。也就是说,图片根本不需要后端处理,前端自己拼接参数就可以处理图片了,非常的方便,而且原图链接也在前端,还可以做长按查看原图功能。

CRUD之天翼云对象存储

1. 安装Java SDK

同样都是通过maven直接引入,但是多了好几个依赖,毕竟不是自研的。

<!-- 天翼云 -->
<dependency><groupId>cn.chinatelecom</groupId><artifactId>oss-java-sdk</artifactId>
<version>2.0.3</version>
</dependency>
<dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.336</version>
</dependency>
<!-- 使用sts服务需要添加以下依赖 -->
<dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-sts</artifactId><version>1.11.336</version>
</dependency>
<dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.3</version>
</dependency>

2. 获取Client

我还是将其变成一个Component

@Component
public class TianyiyunOssClient {@Value("${oss.endpoint}")private String endpoint;@Value("${tianyiyun.accessKeyId}")private String accessKeyId;@Value("${tianyiyun.accessKeySecret}")private String accessKeySecret;@Beanpublic AmazonS3 getOssClient() {BasicAWSCredentials credentials = new BasicAWSCredentials(accessKeyId, accessKeySecret);ClientConfiguration clientConfiguration = new ClientConfiguration();EndpointConfiguration endpointConfiguration = new EndpointConfiguration(endpoint, Regions.DEFAULT_REGION.getName());return AmazonS3ClientBuilder.standard()//客户端设置.withClientConfiguration(clientConfiguration)//凭证设置.withCredentials(new AWSStaticCredentialsProvider(credentials))//endpoint设置.withEndpointConfiguration(endpointConfiguration).build();}
}

3. 增删改查操作

(1)上传对象

官方文档

    /*** 简单上传-流式上传-公共读** @param bucketName  bucket名称* @param key         文件名* @param inputStream 输入流* @return PutObjectResult 上传结果*/public static PutObjectResult putObjectByInputStream(String bucketName, String key, InputStream inputStream) {ObjectMetadata metadata = new ObjectMetadata();metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());// 准备OSS上传对象请求PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, metadata);putObjectRequest.setMetadata(metadata);//设置上传对象的Acl为公共读,私有写putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);// 上传return tianyiyunOssClient.putObject(putObjectRequest);}

基本和阿里云一模一样,这里就不赘述怎么实现公共读和私有读了,核心就是设置setCannedAcl。

(2)获取对象访问链接

官方文档

/*** 生成预签名下载链接** @param bucketName bucket名称* @param key        文件名* @param expiration 过期时间* @return 文件访问链接*/public static String getObjectUrl(String bucketName, String key, Date expiration) {GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key).withMethod(HttpMethod.GET).withExpiration(expiration);return tianyiyunOssClient.generatePresignedUrl(request).toString();}

这里稍微和阿里云不太一样,虽然GeneratePresignedUrlRequest也有setExpiration方法,但好像不生效,必须要使用上面这种形式才可以加签,奇怪🤔。

(3)删除对象

官方文档

/*** 通过bucketName、key删除文件** @param bucketName bucket名称* @param key        文件名*/public static void delObject(String bucketName, String key) {// 删除文件或目录。如果要删除目录,目录必须为空。tianyiyunOssClient.deleteObject(bucketName, key);}

(4)图片处理

官方文档
这里天翼云的文档没有详细说明,但我还是找到了天翼云对象存储是怎么处理图片的。同阿里云的x-oss-process,天翼云的处理参数为x-amz-process。用法也和阿里云的一样,直接把参数拼接在url后面即可。

try {GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey).withMethod(HttpMethod.GET).withExpiration(expiration);// 在URL中添加额外参数request.addRequestParameter("x-amz-limit", String.format("rate=%d", 100));request.addRequestParameter("x-amz-process", "image/watermark,text_12345678");URL url = s3.generatePresignedUrl(request);
} catch (AmazonServiceException e) {System.err.println(e.getErrorMessage());
}

CRUD之MinIO对象存储

1. 安装Java SDK

同上

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

2. 获取Client

同上,注册为Component

@Component
public class MinioOssClient {@Value("${minio.endpoint}")private String endpoint;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;/*** 注入minio 客户端** @return 客户端*/@Beanpublic MinioClient minioClient() {return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).httpClient(getUnsafeOkHttpClient()).build();}private OkHttpClient getUnsafeOkHttpClient() {try {final TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] x509Certificates, String s) throwsCertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] x509Certificates, String s)throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[] {};}}};X509TrustManager x509TrustManager = (X509TrustManager)trustAllCerts[0];final SSLContext sslContext = SSLContext.getInstance("SSL");sslContext.init(null, trustAllCerts, new SecureRandom());final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();OkHttpClient.Builder builder = new OkHttpClient.Builder();builder.sslSocketFactory(sslSocketFactory, x509TrustManager);builder.hostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String s, SSLSession sslSession) {return true;}});return builder.build();} catch (NoSuchAlgorithmException | KeyManagementException e) {throw new RuntimeException(e);}}}

这里多了一个方法getUnsafeOkHttpClient方法,原因是MinIO是我们自建的,使用的https://ip:port的方式去调用,而不是域名方式。对接过这种地址的小伙伴都知道,如果不处理一下https的安全证书,调用的时候就会报错。该方法是为了解决SSL证书验证异常,即当请求的URL使用的是HTTPS协议时,如果证书无效或不被信任,会抛出SSLHandshakeException异常,通过自定义TrustManager和SSLSocketFactory来实现忽略证书的验证,从而避免SSL异常。

3. 增删改查操作

(1)上传对象

 /*** 简单上传-流式上传** @param bucketName  bucket名称* @param key         文件名* @param inputStream 输入流* @return ObjectWriteResponse 上传结果*/public static ObjectWriteResponse uploadInputStream(String bucketName, String key, InputStream inputStream)throws Exception {//设置权限Map<String, String> userMetadata = new HashMap<>();//设置为公有读userMetadata.put("access-control", "public-read");//设置为私有读// userMetadata.put("access-control", "private");// 准备OSS上传对象请求PutObjectArgs putObjectArgs = PutObjectArgs.builder()// bucketName.bucket(bucketName)// 文件名称.object(key)//设置文件权限.userMetadata(userMetadata).stream(inputStream, inputStream.available(), -1).build();// 上传文件return minioClient.putObject(putObjectArgs);}

(2)获取对象访问链接

  /*** 获取文件地址** @param bucketName bucketName* @param key        文件名称* @param duration   过期时长* @param unit       过期时长单位* @return 文件地址*/public static String getObjectUrl(String bucketName, String fileName, int duration, TimeUnit unit)throws Exception {// 查看文件地址return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()// bucketName.bucket(bucketName)// 文件名称.object(fileName)// 过期参数.expiry(duration, unit)// 请求方式.method(Method.GET)// 构建参数.build());}

(3)删除对象

    /*** 通过bucketName、key删除文件** @param bucketName bucket名称* @param key        文件名*/public static void delObject(String bucketName, String key) throws Exception {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(key).build());}

(4)图片处理

很可惜,MinIO没有图片处理的功能。

总结一下

这三种对象存储工具都是我在真实项目中使用过的,它们的使用流程相似,接口名称也差不多,但是在一些细节上还是有些不同的地方。为了让大家更好地了解它们,我在文章中都有标明区别。阿里云和天翼云使用起来比较方便,只需要购买服务就可以了。而MinIO就比较麻烦,需要自己搭建一个服务器环境,并在生产环境中满足主备、证书等方面的要求,这也让我在使用过程中踩了不少坑。不过,我会在新的文章中详细介绍如何搭建MinIO服务器以及如何解决它的问题。

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

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

相关文章

idea禁用双击ctrl

Run anything | IntelliJ IDEA Documentation Disable double modifier key shortcuts

Adaptive Homogeneity-DirectedDemosaicing Algorithm

Abstract 经济高效的数码相机使用单图像传感器&#xff0c;将红色、绿色和蓝色滤色镜的交替图案应用到每个像素位置。通过估计每个颜色平面中缺失的像素分量来重建彩色图像的完整三色表示的方法称为去马赛克算法。本文提出了通常与结合二维 (2-D) 方向插值的去马赛克算法相关的…

密码管理的艺术:数据库存储密码的策略、技术和工具

最近接手公司一个之前的服务&#xff0c;竟然发现用户密码是明文存储在数据库中&#xff01; 说实话还是有点吃惊的&#xff0c;这可不兴学 CSDN 呀&#xff0c;至少也得搞个 MD5 存一存吧。 不过 MD5 其实也没啥用&#xff0c;今天我们就来盘盘密码这种敏感信息该如何存储。…

Goland Cannot use ‘err‘ (type error) as the type any

问题描述&#xff1a; 用Goland写代码的时候&#xff0c;使用panic总是报错&#xff0c;官方用法也是报错&#xff0c;最后找到官方回复的链接&#xff0c;https://youtrack.jetbrains.com/issue/GO-12179/Cannot-use-err-type-error-as-the-type-any 问题解决方式&#xff1…

便利店小程序可以做哪些营销活动呢

在当今这个数字化时代&#xff0c;微信小程序已经成为了人们日常生活的一部分。对于便利店来说&#xff0c;拥有一个优秀的小程序不仅可以提高销售&#xff0c;还可以扩大品牌影响力&#xff0c;增加客户粘性。本文将探讨便利店小程序可以做什么样的营销活动&#xff0c;如何利…

Redis 排障:你永远不知道告警和下班,谁先到来?

01 第一个重点&#xff0c;服务排障的基本方法 在岁月静好的一天&#xff0c;正当笔者准备下班工作的时候&#xff0c;突然&#xff0c;告警出现了&#xff01; 嗯&#xff0c;又是一到下班就会告警&#xff01; 仔细一看&#xff0c;原来是数据整体处理时间的慢了。 既然慢了…

2023年全球新能源云母材料市场发展展望分析:储能云母市场规模快速增长[图]

云母作为电气设备的基础材料&#xff0c;下游应用领域涉及高温冶炼、电力等传统行业&#xff0c;并在近几年逐步扩展到新能源汽车、电化学储能等新兴行业。2022年&#xff0c;全球云母材料市场规模保持稳定增长至180.0亿元&#xff0c;期间年复合增长率约为13.2%。预计未来&…

【Hello Algorithm】暴力递归到动态规划(三)

暴力递归到动态规划&#xff08;三&#xff09; 最长公共子序列递归版本动态规划 最长回文串子序列方法一方法二递归版本动态规划 象棋问题递归版本动态规划 咖啡机问题递归版本动态规划 最长公共子序列 这是leetcode上的一道原题 题目连接如下 最长公共子序列 题目描述如下…

NodeMCU ESP8266 基于Arduino IDE的串口图形化调试教程(超详细)

NodeMCU ESP8266 基于Arduino IDE的串口图形化调试教程 文章目录 NodeMCU ESP8266 基于Arduino IDE的串口图形化调试教程前言Serial Plotter测试前期准备打开工具方法 1方法 2 测试代码 总结 前言 在嵌入式的开发过程中&#xff0c;我们经常会采集一些传感器的数据&#xff0c…

iCloud涨价不用慌!学会使用群晖生态将本地SSD“上云”

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是想使用群晖生态软件&#xff0c;就必须要在服务端安装群晖系统&#xff0c;具体如何安装群晖虚拟机请参考&#xff1a; 1. 安装并配置synology drive1.1 安装群辉drive套件1.2 在局域…

本地PHP搭建简单Imagewheel私人云图床,在外远程访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦 &#x1f516;系列专栏&#xff1a; C语言、Linux &#x1f325;️每日语录&#xff1a;追逐影子的人&#xff0c;自己就是影子。 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 1.前言 云存储在前几年风头无两&#xff0c;云存…

uniapp安卓 华为商店 vivo商店 oppo 小米 上架问题 Android中怎么才能不提前申请权限

问题描述 提交 小米 oppo 市场审核失败&#xff0c;原因是提前向用户申请开启通讯录、定位、短信、录音、相机和日历等权限。 解决方案&#xff1a; 是否有使用 READ_PHONE_STATE 权限&#xff0c;如果有使用 oaid 代替&#xff1b;是否有使用第三方 SDK&#xff0c;如果有关…

外部统一设置了::-webkit-scrollbar { display: none; }如何单独给特定元素开启滚动条设置样式-web页面滚动条样式设置

如果你在外部统一设置了​​::-webkit-scrollbar { display: none; }​​​来隐藏滚动条&#xff0c;但是想要在​​.lever​​元素中单独开启滚动条的样式&#xff0c;你可以使用CSS的级联选择器来覆盖外部样式。 以下是一个示例&#xff0c;展示如何给​​.lever​​单独开启…

什么是实验室超声消泡机?工作原理是怎样的?

超声波消泡设备也叫超声波脱气机、超声波消泡机、超声波消泡器。超声波在液体中产生空化作用,使得液体中溶解的气体(如:空气)不断凝聚,成为很细小的气泡,最后成为球状气泡脱离液体表面&#xff0c;从而达到液体脱气、液体消泡的目的。 实验室超声消泡机工作原理&#xff1a; …

13年测试老鸟,性能测试内存泄露——案例分析(超细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、环境配置 1&a…

第二证券:国际油价大幅上涨 后市恐难持续走高

上个买卖周&#xff0c;受巴以冲突影响&#xff0c;原油商场成为各方关注的焦点。到上星期五收盘&#xff0c;布伦特原油周内涨幅达7%以上&#xff0c;为本年2月以来最大周涨幅&#xff0c;WTI原油周内累计上涨近6%。业内人士认为&#xff0c;其时地缘要素是导致油价出现异动的…

systemverilog/uvm的 blog https://www.amiq.com/consulting/blog/

有很多systemverilog/uvm的 blog https://www.amiq.com/consulting/blog/

字符串排序程序

字符串排序程序&#xff0c;对一个字符串中的数值进行从小到大的排序 例如排序前给定的字符串为" 20 78 9 -7 88 36 29" 排序后&#xff1a; -7 9 20 29 36 78 88 要求使用包装类对数值类型的字符串转换成整型进行排序。 public class StringSort {public static vo…

SpringBoot学习日记

Spring程序与SpringBoot程序对比 SpringBoot程序优点 起步依赖&#xff08;简化依赖配置&#xff09;自动装配&#xff08;简化常用工程相关配置&#xff09;辅助功能&#xff08;内置服务器&#xff0c;......&#xff09; 内嵌Tomcat REST风格 REST简介 REST&#xff0c;表…

.Net Core 6 运行环境手动安装流程

安装.NET Core 6 概述 在开始之前&#xff0c;我们首先需要了解一下整个安装过程的流程。下面的表格将展示安装.NET Core 6的步骤以及每一步需要做的事情。 步骤 动作 说明 1 下载.NET Core 6 SDK 从官方网站下载.NET Core 6 SDK安装包 2 安装.NET Core 6 SDK …