Minio文件分片上传实现

资源准备
MacM1Pro 安装Parallels19.1.0请参考 https://blog.csdn.net/qq_41594280/article/details/135420241
MacM1Pro Parallels安装CentOS7.9请参考 https://blog.csdn.net/qq_41594280/article/details/135420461
部署Minio和整合SpringBoot请参考 https://blog.csdn.net/qq_41594280/article/details/135613722

Minio Paralles虚拟机文件百度网盘获取地址: MinioParallelsVMFile
代码(含前后端)可参考 minio-chunk-upload-demo

# 1.ide拉取代码启动(AppMain)后端服务
# 2.cd vue-minio-upload-sample
# 3.npm install
# 4.npm run dev
# 5.访问 http:127.0.0.1:8080 进行测试

一、准备表结构

1.1 文件上传信息表

CREATE TABLE minio_file_upload_info(
	`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '自增主键',
	`file_name` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '文件名称',
	`file_md5` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '文件MD5',
	`upload_id` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '文件上传Id',
 	`file_url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '文件路径',
 	`total_chunk` INT(10) NOT NULL DEFAULT 0 COMMENT '文件总分块数',
 	`file_status` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '文件状态',
 	`update_time` DATETIME DEFAULT NULL COMMENT '修改时间'
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='文件上传信息';

1.2 分块上传信息表

CREATE TABLE minio_chunk_upload_info(
	`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT '自增主键',
	`chunk_number` INT(10) NOT NULL DEFAULT 0 COMMENT '文件分片号',
	`file_md5` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '文件MD5',
	`upload_id` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '文件上传Id',
 	`chunk_upload_url` VARCHAR(1000) NOT NULL DEFAULT '' COMMENT '文件分片路径',
 	`expiry_time` DATETIME DEFAULT NULL COMMENT '失效时间'
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='文件分片信息';

二、Minio文件相关基操

2.1 Entity

/**
 * 文件上传信息
 */
@TableName(schema = "minio_demo", value = "minio_file_upload_info")
@Data
public class MinioFileUploadInfo {

    /**
     * 自增ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 文件名称
     */
    private String fileName;

    /**
     * 文件Md5值
     */
    private String fileMd5;

    /**
     * 文件上传ID
     */
    private String uploadId;

    /**
     * 文件路径
     */
    private String fileUrl;

    /**
     * 总分块数
     */
    private Integer totalChunk;

    /**
     * 文件上传状态
     */
    private String fileStatus;

    /**
     * 修改时间
     */
    private Date updateTime;

}
/**
 * 文件分片信息
 */
@TableName(schema = "minio_demo", value = "minio_chunk_upload_info")
@Data
public class MinioFileChunkUploadInfo implements Serializable {

    /**
     * 自增ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 文件Md5值
     */
    private String fileMd5;

    /**
     * 上传ID
     */
    private String uploadId;

    /**
     * 文件块号
     */
    private Integer chunkNumber;

    /**
     * 文件块上传URL
     */
    private String chunkUploadUrl;

    /**
     * 过期时间
     */
    private LocalDateTime expiryTime;

}

2.2 Mapper

public interface MinioFileUploadInfoMapper extends MyBaseMapper<MinioFileUploadInfo> {
}
public interface MinioFileChunkUploadInfoMapper extends MyBaseMapper<MinioFileChunkUploadInfo> {
}

2.3 Service

public interface MinioFileUploadInfoService extends IService<MinioFileUploadInfo> {

    /**
     * 根据文件 md5 查询
     *
     * @param fileMd5 文件 md5
     */
    MinioFileUploadInfoDTO getByFileMd5(String fileMd5);

    /**
     * 保存
     *
     * @param param 参数对象
     */
    MinioFileUploadInfoDTO saveMinioFileUploadInfo(MinioFileUploadInfoParam param);

    /**
     * 修改文件状态
     *
     * @param param 参数对象
     */
    int updateFileStatusByFileMd5(MinioFileUploadInfoParam param);

}
@Service
public class MinioFileUploadInfoServiceImpl
        extends ServiceImpl<MinioFileUploadInfoMapper, MinioFileUploadInfo>
        implements MinioFileUploadInfoService {

    @Override
    public MinioFileUploadInfoDTO getByFileMd5(String fileMd5) {
        MinioFileUploadInfo minioFileUploadInfo = this.baseMapper.selectOne(
                new LambdaQueryWrapper<MinioFileUploadInfo>()
                        .eq(MinioFileUploadInfo::getFileMd5, fileMd5));
        if (null == minioFileUploadInfo) {
            return null;
        }
        return ExtBeanUtils.doToDto(minioFileUploadInfo, MinioFileUploadInfoDTO.class);
    }

    @Override
    public MinioFileUploadInfoDTO saveMinioFileUploadInfo(MinioFileUploadInfoParam param) {
        MinioFileUploadInfo minioFileUploadInfo;
        if (null == param.getId()) {
            minioFileUploadInfo = new MinioFileUploadInfo();
        } else {
            minioFileUploadInfo = this.baseMapper.selectById(param.getId());
            if (null == minioFileUploadInfo) {
                throw new MinioDemoException(MinioDemoExceptionTypes.DATA_NOT_EXISTED);
            }
            minioFileUploadInfo.setUpdateTime(new Date());
        }
        BeanUtils.copyProperties(param, minioFileUploadInfo, "id");
        int result;
        if (null == param.getId()) {
            result = this.baseMapper.insert(minioFileUploadInfo);
        } else {
            result = this.baseMapper.updateById(minioFileUploadInfo);
        }
        if (result == 0) {
            throw new MinioDemoException(MinioDemoExceptionTypes.USER_OPERATE_FAILED);
        }
        return ExtBeanUtils.doToDto(minioFileUploadInfo, MinioFileUploadInfoDTO.class);
    }

    @Override
    public int updateFileStatusByFileMd5(MinioFileUploadInfoParam param) {
        MinioFileUploadInfo minioFileUploadInfo = this.baseMapper.selectOne(
                new LambdaQueryWrapper<MinioFileUploadInfo>()
                        .eq(MinioFileUploadInfo::getFileMd5, param.getFileMd5()));
        if (null == minioFileUploadInfo) {
            throw new MinioDemoException(MinioDemoExceptionTypes.DATA_NOT_EXISTED);
        }
        minioFileUploadInfo.setFileStatus(param.getFileStatus());
        minioFileUploadInfo.setFileUrl(param.getFileUrl());
        return this.baseMapper.updateById(minioFileUploadInfo);
    }
}
public interface MinioFileChunkUploadInfoService extends IService<MinioFileChunkUploadInfo> {

    boolean saveMinioFileChunkUploadInfo(MinioFileChunkUploadInfoParam chunkUploadInfoParam);

    List<MinioFileChunkUploadInfoDTO> listByFileMd5AndUploadId(String fileMd5, String uploadId);
}
@Service
public class MinioFileChunkUploadInfoServiceImpl
        extends ServiceImpl<MinioFileChunkUploadInfoMapper, MinioFileChunkUploadInfo>
        implements MinioFileChunkUploadInfoService {

    @Override
    public boolean saveMinioFileChunkUploadInfo(MinioFileChunkUploadInfoParam param) {
        List<MinioFileChunkUploadInfo> list = new ArrayList<>();
        for (int i = 0; i < param.getUploadUrls().size(); i++) {
            MinioFileChunkUploadInfo tempObj = new MinioFileChunkUploadInfo();
            tempObj.setChunkNumber(i + 1);
            tempObj.setFileMd5(param.getFileMd5());
            tempObj.setUploadId(param.getUploadId());
            tempObj.setExpiryTime(param.getExpiryTime());
            tempObj.setChunkUploadUrl(param.getUploadUrls().get(i));
            list.add(tempObj);
        }
        int result = this.baseMapper.insertBatchSomeColumn(list);
        return result != 0;
    }

    @Override
    public List<MinioFileChunkUploadInfoDTO> listByFileMd5AndUploadId(String fileMd5, String uploadId) {
        List<MinioFileChunkUploadInfo> list = this.baseMapper.selectList(
                Wrappers.<MinioFileChunkUploadInfo>lambdaQuery()
                        .select(MinioFileChunkUploadInfo::getChunkUploadUrl)
                        .eq(MinioFileChunkUploadInfo::getFileMd5, fileMd5)
                        .eq(MinioFileChunkUploadInfo::getUploadId, uploadId));
        return ExtBeanUtils.doListToDtoList(list, MinioFileChunkUploadInfoDTO.class);
    }
}

至此 Entity、Mapper、Service 准备完毕

三、Minio分片实现

3.1 文件状态枚举

@Getter
public enum MinioFileStatus {

    UN_UPLOADED("UN_UPLOADED", "待上传"),
    UPLOADED("UPLOADED", "已上传"),
    UPLOADING("", "上传中")
    ;

    final String code;
    final String msg;

    MinioFileStatus(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

3.2 MinioService新增方法

public interface MinioService {

	/**
     * 初始化获取 uploadId
     *
     * @param objectName  文件名
     * @param partCount   分片总数
     * @param contentType contentType
     * @return uploadInfo
     */
    MinioUploadInfo initMultiPartUpload(String objectName,
                                        int partCount,
                                        String contentType);

    /**
     * 分片合并
     *
     * @param objectName 文件名
     * @param uploadId   uploadId
     * @return region
     */
    String mergeMultiPartUpload(String objectName, String uploadId);

    /**
     * 获取已上传的分片列表
     *
     * @param objectName 文件名
     * @param uploadId   uploadId
     * @return 分片列表
     */
    List<Integer> listUploadChunkList(String objectName, String uploadId);
}
@Component
@Slf4j
@RequiredArgsConstructor
public class MinioServiceImpl implements MinioService {

    public MinioUploadInfo initMultiPartUpload(String objectName, int partCount, String contentType) {
        HashMultimap<String, String> headers = HashMultimap.create();
        headers.put("Content-Type", contentType);

        String uploadId = "";
        List<String> partUrlList = new ArrayList<>();
        try {
            // 获取 uploadId
            uploadId = minioClient.getUploadId(minIoClientConfig.getBucketName(),
                    null,
                    objectName,
                    headers,
                    null);
            Map<String, String> paramsMap = new HashMap<>(2);
            paramsMap.put("uploadId", uploadId);
            for (int i = 1; i <= partCount; i++) {
                paramsMap.put("partNumber", String.valueOf(i));
                // 获取上传 url
                String uploadUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        // 注意此处指定请求方法为 PUT,前端需对应,否则会报 `SignatureDoesNotMatch` 错误
                        .method(Method.PUT)
                        .bucket(minIoClientConfig.getBucketName())
                        .object(objectName)
                        // 指定上传连接有效期
                        // .expiry(paramConfig.getChunkUploadExpirySecond(), TimeUnit.SECONDS)
                        .extraQueryParams(paramsMap).build());

                partUrlList.add(uploadUrl);
            }
        } catch (Exception e) {
            log.error("initMultiPartUpload Error:" + e);
            return null;
        }
        // 过期时间 TODO 过期
        LocalDateTime expireTime = LocalDateTime.now().minusHours(1);
        MinioUploadInfo result = new MinioUploadInfo();
        result.setUploadId(uploadId);
        result.setExpiryTime(expireTime);
        result.setUploadUrls(partUrlList);
        return result;
    }

    /**
     * 分片合并
     *
     * @param objectName 文件名
     * @param uploadId   uploadId
     */
    public String mergeMultiPartUpload(String objectName, String uploadId) {
        // todo 最大1000分片 这里好像可以改吧
        Part[] parts = new Part[1000];
        int partIndex = 0;
        ListPartsResponse partsResponse = listUploadPartsBase(objectName, uploadId);
        if (null == partsResponse) {
            log.error("查询文件分片列表为空");
            throw new RuntimeException("分片列表为空");
        }
        for (Part partItem : partsResponse.result().partList()) {
            parts[partIndex] = new Part(partIndex + 1, partItem.etag());
            partIndex++;
        }
        ObjectWriteResponse objectWriteResponse;
        try {
            objectWriteResponse = minioClient.mergeMultipart(minIoClientConfig.getBucketName(), null, objectName, uploadId, parts, null, null);
        } catch (Exception e) {
            log.error("分片合并失败:" + e);
            throw new RuntimeException("分片合并失败:" + e.getMessage());
        }
        if (null == objectWriteResponse) {
            log.error("合并失败,合并结果为空");
            throw new RuntimeException("分片合并失败");
        }
        return objectWriteResponse.region();
    }

    /**
     * 获取已上传的分片列表
     *
     * @param objectName 文件名
     * @param uploadId   uploadId
     */
    public List<Integer> listUploadChunkList(String objectName, String uploadId) {
        ListPartsResponse partsResponse = listUploadPartsBase(objectName, uploadId);
        if (null == partsResponse) {
            return Collections.emptyList();
        }
        return partsResponse.result().partList().stream()
                .map(Part::partNumber).collect(Collectors.toList());
    }


    private ListPartsResponse listUploadPartsBase(String objectName, String uploadId) {
        int maxParts = 1000;
        ListPartsResponse partsResponse;
        try {
            partsResponse = minioClient.listMultipart(minIoClientConfig.getBucketName(), null, objectName, maxParts, 0, uploadId, null, null);
        } catch (ServerException | InsufficientDataException | ErrorResponseException | NoSuchAlgorithmException |
                 IOException | XmlParserException | InvalidKeyException | InternalException |
                 InvalidResponseException e) {
            log.error("查询文件分片列表错误:{},uploadId:{}", e, uploadId);
            return null;
        }
        return partsResponse;
    }

}

3.3 分片文件Service

public interface FileUploadService {

    /**
     * 获取分片上传信息
     *
     * @param param 参数
     * @return Minio上传信息
     */
    MinioUploadInfo getUploadId(GetMinioUploadInfoParam param);

    /**
     * 检查文件是否存在
     *
     * @param md5 md5
     * @return true存在 false不存在
     */
    MinioOperationResult checkFileExistsByMd5(String md5);

    /**
     * 查询已上传的分片序号
     *
     * @param objectName 文件名
     * @param uploadId   uploadId
     * @return 已上传的分片序号列表
     */
    List<Integer> listUploadParts(String objectName, String uploadId);

    /**
     * 分片合并
     *
     * @param param 参数
     * @return url
     */
    String mergeMultipartUpload(MergeMinioMultipartParam param);
}
@Slf4j
@Service
public class FileUploadServiceImpl implements FileUploadService {

    @Resource
    private MinioService minioService;
    @Resource
    private MinioFileUploadInfoService minioFileUploadInfoService;
    @Resource
    private MinioFileChunkUploadInfoService minioFileChunkUploadInfoService;

    @Override
    public MinioUploadInfo getUploadId(GetMinioUploadInfoParam param) {
        MinioUploadInfo uploadInfo;
        MinioFileUploadInfoDTO minioFileUploadInfo = this.minioFileUploadInfoService.getByFileMd5(param.getFileMd5());
        if (null == minioFileUploadInfo) {
            // 计算分片数量
            double partCount = Math.ceil(param.getFileSize() * 1.0 / param.getChunkSize());
            log.info("总分片数:" + partCount);
            uploadInfo = minioService.initMultiPartUpload(param.getFileName(), (int) partCount, param.getContentType());
            if (null != uploadInfo) {
                MinioFileUploadInfoParam saveParam = new MinioFileUploadInfoParam();
                saveParam.setUploadId(uploadInfo.getUploadId());
                saveParam.setFileMd5(param.getFileMd5());
                saveParam.setFileName(param.getFileName());
                saveParam.setTotalChunk((int) partCount);
                saveParam.setFileStatus(MinioFileStatus.UN_UPLOADED.getCode());
                // 保存文件上传信息
                MinioFileUploadInfoDTO minioFileUploadInfoDTO = minioFileUploadInfoService.saveMinioFileUploadInfo(saveParam);
                log.info("文件上传信息保存成功 {}", JSON.toJSONString(minioFileUploadInfoDTO));

                MinioFileChunkUploadInfoParam chunkUploadInfoParam = new MinioFileChunkUploadInfoParam();
                chunkUploadInfoParam.setUploadUrls(uploadInfo.getUploadUrls());
                chunkUploadInfoParam.setUploadId(uploadInfo.getUploadId());
                chunkUploadInfoParam.setExpiryTime(uploadInfo.getExpiryTime());
                chunkUploadInfoParam.setFileMd5(param.getFileMd5());
                chunkUploadInfoParam.setFileName(param.getFileName());
                // 保存分片上传信息
                boolean chunkUploadResult = minioFileChunkUploadInfoService.saveMinioFileChunkUploadInfo(chunkUploadInfoParam);
                log.info("文件分片信息保存{}", chunkUploadResult ? "成功" : "失败");
            }
            return uploadInfo;
        }
        // 查询分片上传地址
        List<MinioFileChunkUploadInfoDTO> list = minioFileChunkUploadInfoService.listByFileMd5AndUploadId(minioFileUploadInfo.getFileMd5(), minioFileUploadInfo.getUploadId());
        List<String> uploadUrlList = list.stream()
                .map(MinioFileChunkUploadInfoDTO::getChunkUploadUrl)
                .collect(Collectors.toList());
        uploadInfo = new MinioUploadInfo();
        uploadInfo.setUploadUrls(uploadUrlList);
        uploadInfo.setUploadId(minioFileUploadInfo.getUploadId());
        return uploadInfo;
    }

    @Override
    public MinioOperationResult checkFileExistsByMd5(String md5) {
        MinioOperationResult result = new MinioOperationResult();
        MinioFileUploadInfoDTO minioFileUploadInfo = this.minioFileUploadInfoService.getByFileMd5(md5);
        if (null == minioFileUploadInfo) {
            result.setStatus(MinioFileStatus.UN_UPLOADED.getCode());
            return result;
        }
        // 已上传
        if (Objects.equals(minioFileUploadInfo.getFileStatus(), MinioFileStatus.UPLOADED.getCode())) {
            result.setStatus(MinioFileStatus.UPLOADED.getCode());
            result.setUrl(minioFileUploadInfo.getFileUrl());
            return result;
        }
        // 查询已上传分片列表并返回已上传列表
        List<Integer> chunkUploadedList = listUploadParts(minioFileUploadInfo.getFileName(), minioFileUploadInfo.getUploadId());
        result.setStatus(MinioFileStatus.UPLOADING.getCode());
        result.setChunkUploadedList(chunkUploadedList);
        return result;
    }

    @Override
    public List<Integer> listUploadParts(String objectName, String uploadId) {
        return minioService.listUploadChunkList(objectName, uploadId);
    }

    @Override
    public String mergeMultipartUpload(MergeMinioMultipartParam param) {
        String result = minioService.mergeMultiPartUpload(param.getFileName(), param.getUploadId());
        if (!StringUtils.isBlank(result)) {
            MinioFileUploadInfoParam fileUploadInfoParam = new MinioFileUploadInfoParam();
            fileUploadInfoParam.setFileUrl(result);
            fileUploadInfoParam.setFileMd5(param.getMd5());
            fileUploadInfoParam.setFileStatus(MinioFileStatus.UPLOADED.getCode());

            // 更新状态
            int updateRows = minioFileUploadInfoService.updateFileStatusByFileMd5(fileUploadInfoParam);
            log.info("update file by file md5 updated count {}", updateRows);
        }
        return result;
    }
}

3.4 Controller新增

@RequestMapping(value = "file")
@RestController
public class FileController {

	@Resource
    private FileUploadService fileUploadService;

    @PostMapping("/upload")
    public R getUploadId(@Validate @RequestBody GetMinioUploadInfoParam param) {
        MinioUploadInfo minioUploadId = fileUploadService.getUploadId(param);
        return R.ok().setData(minioUploadId);
    }

    @GetMapping("/upload/check")
    public R checkFileUploadedByMd5(@RequestParam("md5") String md5) {
        return R.ok().setData(fileUploadService.checkFileExistsByMd5(md5));
    }

    @PostMapping("/upload/merge")
    public R mergeUploadFile(@Validated MergeMinioMultipartParam param) {
        String result = fileUploadService.mergeMultipartUpload(param);
        if (StringUtils.isEmpty(result)) {
            throw new MinioDemoException(MinioDemoExceptionTypes.CHUNK_MERGE_FAILED);
        }
        return R.ok().setData(result); // url
    }
}

3.5 文件分片上传测试

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

select * from minio_file_upload_info;

在这里插入图片描述

select * from minio_chunk_upload_info;

在这里插入图片描述

FAQ

  • 上传失败,code码为403,请同步minio服务器时间。

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

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

相关文章

stm32 FOC系列 直流有刷控制原理

1、直流有刷驱动板 使用三极管搭建的简易 H 桥电路&#xff0c;如图 5.3.1 所示&#xff1a; 图 5.3.1 是使用三极管搭建的简易 H 桥电路&#xff0c;其中 MOTOR 表示直流有刷电机&#xff0c; Q1、 Q2、 Q3 和 Q4 为 4 个三极管&#xff0c;其中 Q1 和 Q3 接在了电源正极&…

Windows如何部署TortoiseSVN客户端

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

GitHub图床TyporaPicGo相关配置

本文作者&#xff1a; slience_me 文章目录 GitHub图床&Typora&PicGo相关配置1. Github配置2. picGo配置3. Typora配置 GitHub图床&Typora&PicGo相关配置 关于Typora旧版的百度网盘下载路径 链接&#xff1a;https://pan.baidu.com/s/12mq-dMqWnRRoreGo4MTbKg?…

DolphinDB学习(1):数据库的增删查与常用操作

下载并配置好DolphinDB&#xff0c;同时添加vscode的插件&#xff0c;我们就在vscode上进行操作 创建xxx.dos文件后&#xff0c;就会被识别为DolphinDB的运行文件&#xff0c;非常方便 文章目录 登录数据库的操作创建数据库查找与删除数据库 示例 登录 如果是vscode&#xff…

微信公众号服务器配置启用, Java示例

微信公众号接入指南文档https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html 在微信公众号的服务器配置中&#xff0c;首先需要编写Java代码来处理微信服务器的验证请求。以下是Java示例代码&#xff0c;然后我将为你提供启用服务器配置…

鸿蒙harmony--数据库sqlite详解

今天是1月20号星期六&#xff0c;早安&#xff0c;岁末大寒至&#xff0c;静后春归来。愿他乡故人&#xff0c;漂泊有归宿&#xff0c;前程有奔赴&#xff0c;愿人间不寒&#xff0c;温暖常伴&#xff0c;诸事顺利&#xff0c;喜乐长安。 目录 一&#xff0c;定义 二&#xff…

华为FusionStorage Block、OceanStor 100D、OceanStor pacific的区别

华为FusionStorage Block、OceanStor 100D、OceanStor pacific的区别&#xff1f; 华为块存储到底是叫什么呢&#xff1f; 有接触过华为块存储产品的小伙伴肯定都有疑惑&#xff0c;在FusionStorage 、FusionStorage Block、OceanStor 100D、OceanStor pacific等等的名词中&a…

mysql函数group_concat()返回结果不全

目录 一、场景二、原因三、排查1、查看group_concat_max_len配置2、修改group_concat_max_len配置a) 如果不方便重启mysql&#xff0c;可以通过以下命令进行配置修改b) 修改mysql配置文件&#xff0c;永久生效 一、场景 mysql使用group_concat()函数拼接返回字段时&#xff0c…

Shell 脚本实现自动启动程序、日志管理和定时任务监控

简介 本篇将通过Shell 脚本实现自动启动Java程序、日志管理和定时任务监控。脚本启动程序具灵活定制、可移植性和扩展性强的优点&#xff0c;可以根据需要添加额外的功能、配置选项和自定义行为&#xff0c;从而满足更具体的要求。 脚本编写 vim start_program.sh#!/bin/bas…

[202401C]巨人之力的题解

原题描述&#xff1a; 时间限制: 1000ms 空间限制: 262144kb 题目描述 两千多年以前&#xff0c;身为艾尔迪亚人的尤弥尔意外获得巨人之力&#xff0c;并且创造了九大巨人&#xff0c;其无以匹敌的力量使得整个世界都陷入了无尽的战乱纷争&#xff0c;艾尔迪亚之外的人类过…

第二百七十八回

文章目录 1. 概念介绍2. 使用方法2.1 DropdownMenu2.1 DropdownMenuEntry 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何禁止页面跟随手机自动旋转"相关的内容&#xff0c;本章回中将介绍DropdownMenu组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1.…

ClickHouse与Doris数据库比较

概述 都说“实践是检验真理的唯一标准”&#xff0c;光说不练假把式&#xff0c;那么本文就通过实际的测试来感受一下Doris和clickhouse在读写方面的性能差距&#xff0c;看看Doris盛名之下&#xff0c;是否真有屠龙之技&#xff1b;clickhouse长锋出鞘&#xff0c;是否敢缚苍…

Cortex-M3/M4内核NVIC及HAL库函数详解(4):使用HAL库配置外部中断

0 工具准备 Keil uVision5 Cortex M3权威指南&#xff08;中文&#xff09; Cortex M3与M4权威指南 stm32f407的HAL库工程 STM32F4xx中文参考手册 1 使用HAL库配置外部中断 前面我们已经熟悉了有关内核部分的寄存器配置&#xff0c;接下来我们结合stm32f407的GPIO外设&#xf…

UE 可靠UDP实现原理

发送 我们的消息发送都是通过 UChannel 来处理的&#xff0c;通过调用 UChannel::SendBunch 统一处理。 发送的 Bunch 是以 FOutBunch 的形式存在的。当 bReliable 为 True 的时候&#xff0c;表示 Bunch 是可靠的。 发送逻辑直接从UChannel::SendBunch处开始分析 1、大小限…

Docker(六)数据管理

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; Docker 数据管理 这一章介绍如何在 Docker 内部以及容器之间管理数据&#xff0c;在容器中管理数据主要有两种方式&#xff1a; 数据卷&…

postman使用-07变量

文章目录 一、变量参数化&#xff08;一&#xff09;、环境变量1、两种方式设置环境变量方法一方法二 2、引用3、选择需要的环境变量 &#xff08;二&#xff09;、参数变量1、全局变量设置全局变量引用查看引用的变量是否是自己设定的值 2、局部变量设置局部变量引用 二、文档…

Halcon基于描述符的模板匹配

Halcon基于描述符的模板匹配 与基于透视形变的模板匹配类似&#xff0c;基于描述符的模板匹配能够在物体处于透视形变的状态下进行匹配&#xff0c;并且已标定和未标定的相机图像都适用。与透视形变不同的是&#xff0c;它的模板不是根据边缘轮廊创建的&#xff0c;而是根据特…

Golang 中如何实现 Set

在Go编程中&#xff0c;数据结构的选择对解决问题至关重要。本文将探讨如何在 GO 中实现 set 和 bitset 两种数据结构&#xff0c;以及它们在Go中的应用场景。 Go 的数据结构 Go 内置的数据结构并不多。工作中&#xff0c;我们最常用的两种数据结构分别是 slice 和 map&#…

Rancher部署k8s集群测试安装nginx(节点重新初始化方法,亲测)

目录 一、安装前准备工作计算机升级linux内核时间同步Hostname设置hosts设置关闭防火墙&#xff0c;selinux关闭swap安装docker 二、安装rancher部署rancher 三、安装k8s安装k8s集群易错点&#xff0c;重新初始化 四、安装kutectl五、测试安装nginx工作负载 一、安装前准备工作…

【排序算法】六、快速排序(C/C++)

「前言」文章内容是排序算法之快速排序的讲解。&#xff08;所有文章已经分类好&#xff0c;放心食用&#xff09; 「归属专栏」排序算法 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 快速排序1.1 原理1.2 Hoare版本&#xff08;单趟&#xff09;1.3 快速排序完整代码&…