MinIo 的操作与使用和避坑

news2024/11/24 7:35:26

文章目录

      • 一、介绍
      • 二、安装
      • 三、Client 连通与避坑
        • 1. SpringBoot 项目
        • 2. 自己的 Maven 项目(非 SpringBoot 项目)
          • 思路1:尝试从 Maven 仓库中替换该 jar 包
          • 思路2:改造有问题的 jar 包
      • 四、封装一些简单的方法

官方 API 文档:Documentation
官方中文文档:MinIO中文文档

一、介绍

  MinIO 是开源的对象存储服务器,相当于免费版的 OSS。

  MinIO 是一款高性能、分布式的开源对象存储系统,它是一款软件产品。MinIO 公司旨在解决非结构化数据增长的需求,开发了流行于业界的开源云存储软件 MinIO。

  虽然 MinIO 是 100% 开源的,但它既是一家公司又是一个开源项目。它采用 GNU AGPL v3 开源证书,拥有 GNU AGPL 代码的版权,同时还是 MinIO 项目的主要贡献者,可独立对 MinIO 进行维护。

  MinIO 基于 Apache License 2.0 开源协议的对象存储服务。它兼容 Amazon S3 云存储接口。适合存储非结构化数据,如图片,音频,视频,日志等。

二、安装

官网下载链接:官网下载界面
在这里插入图片描述

# 下载安装包
[root@xiaoqiang ~]# wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio_20230930070229.0.0_amd64.deb -O minio.deb
[root@xiaoqiang  ~]# sudo dpkg -i minio.deb

# 设置
[root@xiaoqiang  ~]# sudo touch  /etc/default/minio
[root@xiaoqiang  ~]# cat /etc/default/minio 
MINIO_ROOT_USER="xiaoiang"
MINIO_ROOT_PASSWORD="heheda123"
MINIO_VOLUMES="/dataspace/xiaoqiang-data-backup"

# 创建用户和组,分配目录权限
[root@xiaoqiang  ~]# sudo groupadd -r minio-user
[root@xiaoqiang  ~]# sudo useradd -M -r -g minio-user minio-user
[root@xiaoqiang  ~]# sudo chown minio-user:minio-user -R /dataspace/xiaoqiang-data-backup

# 启动服务
[root@xiaoqiang  ~]# sudo systemctl daemon-reload
[root@xiaoqiang  ~]# sudo systemctl enable minio.service
[root@xiaoqiang  ~]# sudo systemctl start minio.service

注意: minio 与 Prometheus、clickhouse 端口冲突,如果启动服务失败,注意关掉相关服务。 netstat -antulp | grep 9000,kill 掉该进程,minio 服务可以重启成功。

在这里插入图片描述

参考:MinIO使用及整合起步依赖

三、Client 连通与避坑

  Java 代码:

        MinioClient minioClient =
                MinioClient.builder()
                        .endpoint("http://192.168.110.110:9000")
                        .credentials("xiaoqiang", "heheda123")
                        .build();
        System.out.println(minioClient);
        // 检查桶是否存在
        boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("test").build());
        if (!found) {
            // 创建桶
            minioClient.makeBucket(MakeBucketArgs.builder().bucket("test").build());
        }

        //列出所有桶名
        List<Bucket> buckets = minioClient.listBuckets();
        for (Bucket i : buckets){
            System.out.println(i.name());
        }

  Maven 引入:

        <!--此依赖为minio的服务依赖-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.5.7</version>
        </dependency>

  报错:Unsupported OkHttp library found. Must use okhttp >= 4.11.0

在这里插入图片描述

        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.5.2</version>
        </dependency>
        <!--或者-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.4.5</version>
        </dependency>
        <!--或者-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.3.7</version>
        </dependency>

  报错:Unsupported OkHttp library found. Must use okhttp >= 4.8.1

在这里插入图片描述

        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.2.1</version>
        </dependency>
        <!--或者-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.1.0</version>
        </dependency>
        <!--或者-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.0.3</version>
        </dependency>

  报错:Exception in thread "main" java.lang.NoSuchMethodError: kotlin.collections.ArraysKt.copyInto([B[BIII)[B

在这里插入图片描述
  尝试解决1:

在这里插入图片描述
  结论:不好使。

  尝试解决2:新建一个纯 Maven 项目。好使

在这里插入图片描述
在这里插入图片描述
  接下来的解决思路:将项目中的依赖依次注释最终只留 Minio 的依赖,看到底是哪个引入的依赖和 Minio 的依赖有冲突。

1. SpringBoot 项目

  最终发现是 spring-boot-starter-parent 的依赖导致的。

在这里插入图片描述
在这里插入图片描述

  居然是 SpringBoot 的顶层依赖导致的,也是绝了。尝试了下低版本居然可以。

在这里插入图片描述

  更高的版本居然也可以。

在这里插入图片描述
  经测试,框起来的版本可以用。
在这里插入图片描述

2. 自己的 Maven 项目(非 SpringBoot 项目)

  经排查,居然是 phoenix 的引入导致的,该 jar 包是从 Cloudera Manager 集群中下载后通过 mvn install 命令手动引入依赖,网上 Maven 仓库并没有这个 jar 包,现在该 jar 包中的 okio 和 minio 中的 okio 冲突导致的。报错:Exception in thread "main" java.lang.NoSuchFieldError: Companion

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  而且通过 exclusion 排除该 jar 包的方式并不好使,可能得重新编译该 jar 包。

思路1:尝试从 Maven 仓库中替换该 jar 包

在这里插入图片描述
在这里插入图片描述
  但总是导入一个依赖就缺另一个依赖。

思路2:改造有问题的 jar 包

  打开 phoenix-5.0.0-cdh6.2.0-client.jar 文件删除 okio 再重新手动安装。测试后发现该方法管用。
在这里插入图片描述

四、封装一些简单的方法

来自:minio的基本使用——java

package com.jdh.minio.config;
 
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
 
/**
 * @ClassName: MinioFile
 * @Author: jdh
 * @CreateTime: 2022-04-15
 * @Description:
 */
@Configuration
@Slf4j
public class MinioFileUtil {
 
    @Resource
    private MinioProperties minioProperties;
 
    private MinioClient minioClient;
 
    /**
     * 这个是6.0.左右的版本
     * @return MinioClient
     */
//    @Bean
//    public MinioClient getMinioClient(){
//
//        String url = "http:" + minioProperties.getIp() + ":" + minioProperties.getPort();
//
//        try {
//            return new MinioClient(url, minioProperties.getAccessKey(), minioProperties.getSecretKey());
//        } catch (InvalidEndpointException | InvalidPortException e) {
//            e.printStackTrace();
//            log.info("-----创建Minio客户端失败-----");
//            return null;
//        }
//    }
 
    /**
     * 下面这个和上面的意思差不多,但是这个是新版本
     * 获取一个连接minio服务端的客户端
     *
     * @return MinioClient
     */
    @Bean
    public MinioClient getClient() {
 
        String url = "http:" + minioProperties.getIp() + ":" + minioProperties.getPort();
        MinioClient minioClient = MinioClient.builder()
                .endpoint(url)    //两种都可以,这种全路径的其实就是下面分开配置一样的
//                        .endpoint(minioProperties.getIp(),minioProperties.getPort(),minioProperties.getSecure())
                .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                .build();
        this.minioClient = minioClient;
        return minioClient;
    }
 
    /**
     * 创建桶
     *
     * @param bucketName 桶名称
     */
    public void createBucket(String bucketName) throws Exception {
        if (!StringUtils.hasLength(bucketName)) {
            throw new RuntimeException("创建桶的时候,桶名不能为空!");
        }
 
        // Create bucket with default region.
        minioClient.makeBucket(MakeBucketArgs.builder()
                .bucket(bucketName)
                .build());
    }
 
    /**
     * 创建桶,固定minio容器
     *
     * @param bucketName 桶名称
     */
    public void createBucketByRegion(String bucketName, String region) throws Exception {
        if (!StringUtils.hasLength(bucketName)) {
            throw new RuntimeException("创建桶的时候,桶名不能为空!");
        }
        MinioClient minioClient = this.getClient();
 
        // Create bucket with specific region.
        minioClient.makeBucket(MakeBucketArgs.builder()
                .bucket(bucketName)
                .region(region) //
                .build());
 
//        // Create object-lock enabled bucket with specific region.
//        minioClient.makeBucket(
//                MakeBucketArgs.builder()
//                        .bucket("my-bucketname")
//                        .region("us-west-1")
//                        .objectLock(true)
//                        .build());
    }
 
    /**
     * 修改桶名
     * (minio不支持直接修改桶名,但是可以通过复制到一个新的桶里面,然后删除老的桶)
     *
     * @param oldBucketName 桶名称
     * @param newBucketName 桶名称
     */
    public void renameBucket(String oldBucketName, String newBucketName) throws Exception {
        if (!StringUtils.hasLength(oldBucketName) || !StringUtils.hasLength(newBucketName)) {
            throw new RuntimeException("修改桶名的时候,桶名不能为空!");
        }
 
    }
 
    /**
     * 删除桶
     *
     * @param bucketName 桶名称
     */
    public void deleteBucket(String bucketName) throws Exception {
        if (!StringUtils.hasLength(bucketName)) {
            throw new RuntimeException("删除桶的时候,桶名不能为空!");
        }
 
        minioClient.removeBucket(
                RemoveBucketArgs.builder()
                        .bucket(bucketName)
                        .build());
    }
 
    /**
     * 检查桶是否存在
     *
     * @param bucketName 桶名称
     * @return boolean true-存在 false-不存在
     */
    public boolean checkBucketExist(String bucketName) throws Exception {
        if (!StringUtils.hasLength(bucketName)) {
            throw new RuntimeException("检测桶的时候,桶名不能为空!");
        }
 
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }
 
    /**
     * 列出所有的桶
     *
     * @return 所有桶名的集合
     */
    public List<Bucket> getAllBucketInfo() throws Exception {
 
        //列出所有桶
        List<Bucket> buckets = minioClient.listBuckets();
        return buckets;
    }
 
    /**
     * 列出某个桶中的所有文件名
     * 文件夹名为空时,则直接查询桶下面的数据,否则就查询当前桶下对于文件夹里面的数据
     *
     * @param bucketName 桶名称
     * @param folderName 文件夹名
     * @param isDeep     是否递归查询
     */
    public Iterable<Result<Item>> getBucketAllFile(String bucketName, String folderName, Boolean isDeep) throws Exception {
        if (!StringUtils.hasLength(bucketName)) {
            throw new RuntimeException("获取桶中文件列表的时候,桶名不能为空!");
        }
        if (!StringUtils.hasLength(folderName)) {
            folderName = "";
        }
        System.out.println(folderName);
        Iterable<Result<Item>> listObjects = minioClient.listObjects(
                ListObjectsArgs
                        .builder()
                        .bucket(bucketName)
                        .prefix(folderName + "/")
                        .recursive(isDeep)
                        .build());
 
//        for (Result<Item> result : listObjects) {
//            Item item = result.get();
//            System.out.println(item.objectName());
//        }
 
        return listObjects;
    }
 
    /**
     * 删除文件夹
     *
     * @param bucketName 桶名
     * @param objectName 文件夹名
     * @param isDeep     是否递归删除
     * @return
     */
    public Boolean deleteBucketFile(String bucketName, String objectName) {
        if (!StringUtils.hasLength(bucketName) || !StringUtils.hasLength(objectName)) {
            throw new RuntimeException("删除文件的时候,桶名或文件名不能为空!");
        }
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
            return true;
        } catch (Exception e) {
            log.info("删除文件失败");
            return false;
        }
    }
 
    /**
     * 删除文件夹
     *
     * @param bucketName 桶名
     * @param objectName 文件夹名
     * @param isDeep     是否递归删除
     * @return
     */
    public Boolean deleteBucketFolder(String bucketName, String objectName, Boolean isDeep) {
        if (!StringUtils.hasLength(bucketName) || !StringUtils.hasLength(objectName)) {
            throw new RuntimeException("删除文件夹的时候,桶名或文件名不能为空!");
        }
        try {
            ListObjectsArgs args = ListObjectsArgs.builder().bucket(bucketName).prefix(objectName + "/").recursive(isDeep).build();
            Iterable<Result<Item>> listObjects = minioClient.listObjects(args);
            listObjects.forEach(objectResult -> {
                try {
                    Item item = objectResult.get();
                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(item.objectName()).build());
                } catch (Exception e) {
                    log.info("删除文件夹中的文件异常", e);
                }
            });
            return true;
        } catch (Exception e) {
            log.info("删除文件夹失败");
            return false;
        }
    }
 
    /**
     * 获取文件下载地址
     *
     * @param bucketName 桶名
     * @param objectName 文件名
     * @param expires    过期时间,默认秒
     * @return
     * @throws Exception
     */
    public String getFileDownloadUrl(String bucketName, String objectName, Integer expires) throws Exception {
 
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)//下载地址的请求方式
                .bucket(bucketName)
                .object(objectName)
                .expiry(expires, TimeUnit.SECONDS)//下载地址过期时间
                .build();
        String objectUrl = minioClient.getPresignedObjectUrl(args);
        return objectUrl;
    }
 
    /**
     * 获取文件上传地址(暂时还未实现)
     *
     * @param bucketName 桶名
     * @param objectName 文件名
     * @param expires    过期时间,默认秒
     * @return
     * @throws Exception
     */
    public String getFileUploadUrl(String bucketName, String objectName, Integer expires) throws Exception {
 
        // 过期时间
        ZonedDateTime zonedDateTime = ZonedDateTime.now().plusSeconds(60);
        PostPolicy postPolicy = new PostPolicy(bucketName, zonedDateTime);
 
        // 获取对象的默认权限策略
        StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        String objectPolicy = statObjectResponse.headers().get("x-amz-object-policy");
 
        String presignedObjectUrl = minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .method(Method.POST)
                        .expiry(expires) // 预签名的 URL 有效期为 1 小时
                        .build());
 
        MyMinioClient client = new MyMinioClient(minioClient);
 
        return presignedObjectUrl;
    }
 
    /**
     * 创建文件夹
     *
     * @param bucketName 桶名
     * @param folderName 文件夹名称
     * @return
     * @throws Exception
     */
    public ObjectWriteResponse createBucketFolder(String bucketName, String folderName) throws Exception {
 
        if (!checkBucketExist(bucketName)) {
            throw new RuntimeException("必须在桶存在的情况下才能创建文件夹");
        }
        if (!StringUtils.hasLength(folderName)) {
            throw new RuntimeException("创建的文件夹名不能为空");
        }
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                .bucket(bucketName)
                .object(folderName + "/")
                .stream(new ByteArrayInputStream(new byte[0]), 0, 0)
                .build();
        ObjectWriteResponse objectWriteResponse = minioClient.putObject(putObjectArgs);
 
 
        return objectWriteResponse;
    }
 
    /**
     * 检测某个桶内是否存在某个文件
     *
     * @param objectName 文件名称
     * @param bucketName 桶名称
     */
    public boolean getBucketFileExist(String objectName, String bucketName) throws Exception {
        if (!StringUtils.hasLength(objectName) || !StringUtils.hasLength(bucketName)) {
            throw new RuntimeException("检测文件的时候,文件名和桶名不能为空!");
        }
 
        try {
            // 判断文件是否存在
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()) &&
                    minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()) != null;
            return exists;
        } catch (ErrorResponseException e) {
            log.info("文件不存在 ! Object does not exist");
            return false;
        } catch (Exception e) {
            throw new Exception(e);
        }
    }
 
    /**
     * 判断桶中是否存在文件夹
     *
     * @param bucketName 同名称
     * @param objectName 文件夹名称
     * @param isDeep     是否递归查询(暂不支持)
     * @return
     */
    public Boolean checkBucketFolderExist(String bucketName, String objectName, Boolean isDeep) {
 
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(isDeep).build());
 
        return results.iterator().hasNext(); // 文件夹下存在文件
    }
 
    /**
     * 根据MultipartFile file上传文件
     * minio 采用文件流上传,可以换成下面的文件上传
     *
     * @param file       上传的文件
     * @param bucketName 上传至服务器的桶名称
     */
    public boolean uploadFile(MultipartFile file, String bucketName) throws Exception {
 
        if (file == null || file.getSize() == 0 || file.isEmpty()) {
            throw new RuntimeException("上传文件为空,请重新上传");
        }
 
        if (!StringUtils.hasLength(bucketName)) {
            log.info("传入桶名为空,将设置默认桶名:" + minioProperties.getBucketName());
            bucketName = minioProperties.getBucketName();
            if (!this.checkBucketExist(minioProperties.getBucketName())) {
                this.createBucket(minioProperties.getBucketName());
            }
        }
 
        if (!this.checkBucketExist(bucketName)) {
            throw new RuntimeException("当前操作的桶不存在!");
        }
 
        // 获取上传的文件名
        String filename = file.getOriginalFilename();
        assert filename != null;
        //可以选择生成一个minio中存储的文件名称
        String minioFilename = UUID.randomUUID().toString() + "_" + filename;
        String url = "http:" + minioProperties.getIp() + ":" + minioProperties.getPort();
 
        InputStream inputStream = file.getInputStream();
        long size = file.getSize();
        String contentType = file.getContentType();
 
        // Upload known sized input stream.
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName) //上传到指定桶里面
                        .object(minioFilename)//文件在minio中存储的名字
                        //p1:上传的文件流;p2:上传文件总大小;p3:上传的分片大小
                        .stream(inputStream, size, -1) //上传分片文件流大小,如果分文件上传可以采用这种形式
                        .contentType(contentType) //文件的类型
                        .build());
 
        return this.getBucketFileExist(minioFilename, bucketName);
    }
 
    /**
     * 上传本地文件,根据路径上传
     * minio 采用文件内容上传,可以换成上面的流上传
     *
     * @param filePath 上传本地文件路径
     * @Param bucketName 上传至服务器的桶名称
     */
    public boolean uploadPath(String filePath, String bucketName) throws Exception {
 
        File file = new File(filePath);
        if (!file.isFile()) {
            throw new RuntimeException("上传文件为空,请重新上传");
        }
 
        if (!StringUtils.hasLength(bucketName)) {
            log.info("传入桶名为空,将设置默认桶名:" + minioProperties.getBucketName());
            bucketName = minioProperties.getBucketName();
            if (!this.checkBucketExist(minioProperties.getBucketName())) {
                this.createBucket(minioProperties.getBucketName());
            }
        }
 
        if (!this.checkBucketExist(bucketName)) {
            throw new RuntimeException("当前操作的桶不存在!");
        }
 
        String minioFilename = UUID.randomUUID().toString() + "_" + file.getName();//获取文件名称
        String fileType = minioFilename.substring(minioFilename.lastIndexOf(".") + 1);
 
        minioClient.uploadObject(
                UploadObjectArgs.builder()
                        .bucket(bucketName)
                        .object(minioFilename)//文件存储在minio中的名字
                        .filename(filePath)//上传本地文件存储的路径
                        .contentType(fileType)//文件类型
                        .build());
 
        return this.getBucketFileExist(minioFilename, bucketName);
    }
 
    /**
     * 文件下载,通过http返回,即在浏览器下载
     *
     * @param response   http请求的响应对象
     * @param bucketName 下载指定服务器的桶名称
     * @param objectName 下载的文件名称
     */
    public void downloadFile(HttpServletResponse response, String bucketName, String objectName) throws Exception {
        if (response == null || !StringUtils.hasLength(bucketName) || !StringUtils.hasLength(objectName)) {
            throw new RuntimeException("下载文件参数不全!");
        }
 
        if (!this.checkBucketExist(bucketName)) {
            throw new RuntimeException("当前操作的桶不存在!");
        }
 
        //获取一个下载的文件输入流操作
        GetObjectResponse objectResponse = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
 
        OutputStream outputStream = response.getOutputStream();
        int len = 0;
        byte[] buf = new byte[1024 * 8];
        while ((len = objectResponse.read(buf)) != -1) {
            outputStream.write(buf, 0, len);
        }
        if (outputStream != null) {
            outputStream.close();
            outputStream.flush();
        }
        objectResponse.close();
    }
 
    /**
     * 文件下载到指定路径
     *
     * @param downloadPath 下载到本地路径
     * @param bucketName   下载指定服务器的桶名称
     * @param objectName   下载的文件名称
     */
    public void downloadPath(String downloadPath, String bucketName, String objectName) throws Exception {
        if (downloadPath.isEmpty() || !StringUtils.hasLength(bucketName) || !StringUtils.hasLength(objectName)) {
            throw new RuntimeException("下载文件参数不全!");
        }
 
        if (!new File(downloadPath).isDirectory()) {
            throw new RuntimeException("本地下载路径必须是一个文件夹或者文件路径!");
        }
 
        if (!this.checkBucketExist(bucketName)) {
            throw new RuntimeException("当前操作的桶不存在!");
        }
 
        downloadPath += objectName;
 
        minioClient.downloadObject(
                DownloadObjectArgs.builder()
                        .bucket(bucketName) //指定是在哪一个桶下载
                        .object(objectName)//是minio中文件存储的名字;本地上传的文件是user.xlsx到minio中存储的是user-minio,那么这里就是user-minio
                        .filename(downloadPath)//需要下载到本地的路径,一定是带上保存的文件名;如 d:\\minio\\user.xlsx
                        .build());
    }
 
}

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

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

相关文章

阿里云域名解析到非默认端口处理方式

1.需配置两条解析记录&#xff0c;如下图 2.第一条配置A记录&#xff0c;ip指向部署服务器 3.第二条配置隐形记录&#xff0c;指向第一条的网址&#xff0c;并附带端口号&#xff0c;最终访问第二条的网址就不用带非默认端口号了。 4.最终浏览器访问

腾讯云轻量应用服务器挂载对象存储新手入门教程

腾讯云轻量对象存储LighthouseCOS是腾讯云专为中小企业开发者打造的轻量级数据存储服务&#xff0c;适用于云端网站、小程序、课堂演示、云盘/图床等场景下的数据存储和处理任务。腾讯云百科txybk.com详细介绍腾讯云轻量对象存储使用、开通和收费价格说明&#xff1a; 轻量对象…

家用超声波清洗机哪个牌子好?一起来看、值得推荐超声波清洗机

家用超声波清洗机可以干嘛呢&#xff1f;最常见的就是来清洗眼镜。眼镜党朋友应该经常接触超声波清洗机&#xff0c;它常出现在眼镜店中&#xff0c;眼镜店老板帮顾客清洗眼镜&#xff1b;也会出现在工业领域、医疗领域等&#xff0c;超声波清洗机使用范围还是挺广的&#xff0…

MIT6S081-Lab2总结

大家好&#xff0c;我叫徐锦桐&#xff0c;个人博客地址为www.xujintong.com&#xff0c;github地址为https://github.com/xjintong。平时记录一下学习计算机过程中获取的知识&#xff0c;还有日常折腾的经验&#xff0c;欢迎大家访问。 Lab2就是了解一下xv6的系统调用流程&…

Beam Search学习

BackGround 在生成的时候&#xff0c;模型的输出是一个时间步一个时间步依次获得的&#xff0c;而且前面时间步的结果还会影响后面时间步的结果。也就是说&#xff0c;每一个时间步&#xff0c;模型给出的都是基于历史生成结果的条件概率。 为了生成完整的句子&#xff0c;需…

Leetcode—2477.到达首都的最少油耗【中等】

2023每日刷题&#xff08;五十&#xff09; Leetcode—2477.到达首都的最少油耗 算法思想 参考自灵茶山艾府 实现代码 class Solution { public:long long minimumFuelCost(vector<vector<int>>& roads, int seats) {int n roads.size() 1;vector<i…

【开源】基于JAVA的桃花峪滑雪场租赁系统

项目编号&#xff1a; S 036 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S036&#xff0c;文末获取源码。} 项目编号&#xff1a;S036&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 游客服务2.2 雪场管理 三、数据库设…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion写好提示词

Stable Diffusion 是一种文生图 AI 模型,由互联网上数百万图像和文本描述对训练而来,通过理解文本描述与图像信息的内在关联,不断利用扩散过程进而得到满意的生成图片。 比如,通过一串提示词,midjourney 会输出这样的情侣合照: A pair of young Chinese lovers, wearing…

Docker部署.NET6项目

Docker的三大核心概念 1、docker仓库&#xff08;repository&#xff09; docker仓库&#xff08;repository&#xff09;类似于代码库&#xff0c;是docker集中存放镜像的场所。实际上&#xff0c;注册服务器是存放仓库的地方&#xff0c;其上往往存放着很多仓库。每个仓库集…

C++学习之路(十五)C++ 用Qt5实现一个工具箱(增加16进制颜色码转换和屏幕颜色提取功能)- 示例代码拆分讲解

上篇文章&#xff0c;我们用 Qt5 实现了在小工具箱中添加了《Base64图片编码预览功能》功能。为了继续丰富我们的工具箱&#xff0c;今天我们就再增加两个平时经常用到的功能吧&#xff0c;就是「 16进制颜色码转RGB文本 」和 「屏幕颜色提取」功能。下面我们就来看看如何来规划…

Postman可以卸载了!这款IDEA插件太好用了!

Postman是大家最常用的API调试工具&#xff0c;那么有没有一种方法可以不用手动写入接口到Postman&#xff0c;即可进行接口调试操作&#xff1f;今天给大家推荐一款IDEA插件&#xff1a;Apipost Helper&#xff0c;写完代码就可以调试接口并一键生成接口文档&#xff01;而且还…

C++笔试题之回文数的判断

“回文”是指正读反读都能读通的句子&#xff0c;它是古今中外都有的一种修辞方式和文字游戏&#xff0c;如“我为人人&#xff0c;人人为我”等。在数学中也有这样一类数字有这样的特征&#xff0c;成为回文数&#xff08;palindrome number&#xff09;。 设n是一任意自然数…

vue3中使用antv-X6实现关系图

先看效果图&#xff1a; 1、安装 npm installantv/x6 --save 这里使用的X6中自定义节点的方式实现的&#xff0c;项目目录如下&#xff0c;hooks里面存放一些函数和变量 nodes里面是自定义节点的页面&#xff0c;最外围的index.vue就是主渲染页面 2、testSh/index.vue 注意&…

计算机视觉GPT时刻!UC伯克利三巨头祭出首个纯CV大模型,推理惊现AGI火花

计算机视觉的GPT时刻&#xff0c;来了&#xff01; 最近&#xff0c;来自UC伯克利的计算机视觉「三巨头」联手推出了第一个无自然语言的纯视觉大模型&#xff08;Large Vision Models&#xff09;&#xff0c;并且第一次证明了纯视觉模型本身也是可扩展的&#xff08;scalabil…

YOLOv8创新魔改教程(三)如何添加注意力机制注意力机制的用法与思考

注意力机制的用法与思考 好多同学问我加了CA注意力机制&#xff0c;CBAM注意力机制&#xff0c;都没有涨点&#xff0c;然后就在不停地换不同的注意力机制&#xff0c;其实并不是这样的。今天和大家讨论一下注意力机制的用法与思考。 &#xff08;一&#xff09;添加位置 大…

异常追踪与 JIRA 实现双向联动

前言 当应用程序或系统出现异常时&#xff0c;通常需要及时处理以保证系统的正常运行。通过异常追踪与 JIRA 双向联动&#xff0c;可以让企业内部相关人员快速了解、分析问题故障发生的原因、追溯并记录故障的处理过程&#xff0c;有效提高人员的沟通效率&#xff0c;极大降低…

使用 SVN 新建本地仓库和提交

在写代码的时候经常要看自己修改了什么或者临时备份一下&#xff0c;发现 SVN 比 Git 更好用&#xff0c;下面是使用 TortoiseSVN 在本地新建仓库和关联仓库的方法。 1. 新建文件夹&#xff0c;在文件夹里面右键选择 TortoiseSVN -> Create repository here 。 2. 选择建立…

C++红黑树封装set和map(很详细)

前言 在前面&#xff0c;我们学习了红黑树。&#xff08;没学过红黑树直接看会很吃力&#xff09;set和map的底层就是红黑树&#xff0c;现在我们要用这棵树来封装STL里面的容器&#xff1a;set和map。 下面是之前讲过的红黑树&#xff0c;他只是普通的“Key”模型,适合封装set…

排序:直接插入排序希尔排序

目录 排序&#xff1a; 概念&#xff1a; 直接插入排序&#xff1a; 代码的实现&#xff1a; 代码解析&#xff1a; 总结&#xff1a; 希尔排序&#xff1a; 代码实现&#xff1a; 预排序&#xff1a; 代码优化&#xff1a; gap 的 本质 &#xff1a; 直接…

千梦网创:设计一个100%回本赚钱的培训模式

互联网上搞培训&#xff0c;牌坊立的快&#xff0c;倒的也快。 你会的东西你教了才能赚钱&#xff0c;你想赚钱你就要教。 你只要教了&#xff0c;立马就有人模仿&#xff0c;立马就有人收费比你更低。 所以&#xff0c;你不会变&#xff0c;你没有核心的东西&#xff0c;就…