Minio文件分片上传实现

news2024/11/27 12:32:40

资源准备
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服务器时间。

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

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

相关文章

BlueBunny:基于低功耗蓝牙的Bash bunny命令控制C2框架

关于BlueBunny BlueBunny是一款功能强大的命令控制框架&#xff0c;该工具基于低功耗蓝牙实现数据通信&#xff0c;可以帮助广大研究人员直接通过蓝牙将控制指令发送给Bash Bunny。 什么是Bash Bunny Bash Bunny是一款类似于USB Rubber Ducky的多功能USB攻击工具&#xff08;…

认识并使用LlamaIndex

认识并使用LlamaIndex 一、认识LlamaIndex1、是什么2、为什么要搞Llama Index&#xff1f;3、怎么搞Llama Index&#xff1f;3.1 方案1&#xff1a;用你的数据对LLM进行微调&#xff08;fine-tune&#xff09;3.2 方案2&#xff1a;[检索增强生成&#xff08;RAG&#xff09;](…

实验六 模式对象管理与安全管理

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

ChatGPT 到 Word:使用 Writage 进行复制粘贴魔法

ChatGPT 到 Word&#xff1a;使用 Writage 进行复制粘贴魔法 写在前面Writage的使用 写在前面 随着ChatGPT的日益普及&#xff0c;越来越多的人每天依赖它来完成各种任务。无论是寻找信息、语言翻译、解决数学问题&#xff0c;还是精炼复杂的概念和文本&#xff0c;ChatGPT 都…

Redis 存在线程安全问题吗?为什么?

一个工作了 5 年的粉丝私信我。 他说自己准备了半年时间&#xff0c;想如蚂蚁金服&#xff0c;结果第一面就挂了&#xff0c;非常难过。 问题是&#xff1a; “Redis 存在线程安全问题吗&#xff1f;” 一、问题解析 关于这个问题&#xff0c;我从两个方面来回答。 第一个&a…

高效调试!利用IDEA远程Debug服务器微服务Jar,快速定位和解决代码问题!

开两个端口端口8081、8085 解释8081 服务端口 8085 远程debug监听端口 服务启动jar命令&#xff1a; java -agentlib:jdwptransportdt_socket,servery,suspendn,address48081 -jar test-server.jar &或者nohup nohup java -agentlib:jdwptransportdt_socket,servery,susp…

c++函数重载(同名函数)功能,区别于c语言

c可以使用同名函数&#xff0c;实现功能类似的多个功能 规则&#xff1a; ①函数名相同&#xff0c;但是函数的参数&#xff08;形参&#xff09;绝不相同 ②参数个数不同 ③参数个数相同&#xff0c;参数类型不同 只有返回值类型不同&#xff0c;不可以&#xff1b;只有形…

初识计算机图形学

初识计算机图形学 笔记来源&#xff1a;【老奇】阴差阳错 撼动世界的游戏引擎 1.MVP Transformation 详见本人博客&#xff1a; 1.Transformation 2.梳理从MVP变换到光栅化的过程 MVP变换将空间中3D物体投影到2D屏幕 2.Rasterization 详见本人博客&#xff1a; 1.Rasteri…

索引的概述和性能分析

索引index&#xff0c;是一种有序的数据结构&#xff0c;可以高效的获取数据&#xff0c;在数据库中维护着满足查找特定算法的数据结构&#xff0c;就是索引 无索引的情况&#xff0c;查询数据时会全表扫描&#xff0c;效率极低 索引结构 &#xff08;1&#xff09;二叉树&…

Django(八)

1. 管理员操作 1.1 添加 from django.shortcuts import render, redirectfrom app01 import models from app01.utils.pagination import Paginationfrom django import forms from django.core.exceptions import ValidationError from app01.utils.bootstrap import BootStr…

使用 crypto-js 进行 AES 加解密操作

在前端开发中&#xff0c;数据的加密和解密是为了保障用户隐私和数据的安全性而常见的任务。AES&#xff08;Advanced Encryption Standard&#xff09;是一种对称密钥加密算法&#xff0c;被广泛用于保护敏感信息的传输和存储。本文将介绍 AES 加解密的基本原理&#xff0c;并…

区块链游戏概览:回顾 2023,展望 2024

作者&#xff1a;lesleyfootprint.network 数据来源&#xff1a;区块链游戏年报 关键要点 今年&#xff0c;比特币&#xff08;BTC&#xff09;市值从上年的低谷中强劲回升&#xff0c;相较之下&#xff0c;区块链游戏市场的增长虽然较为平淡&#xff0c;但年末也迎来了显著的…

【长文阅读】MAMBA作者博士论文<MODELING SEQUENCES WITH STRUCTURED STATE SPACES>-Chapter2

Gu A. Modeling Sequences with Structured State Spaces[D]. Stanford University, 2023. 本文是MAMBA作者的博士毕业论文&#xff0c;为了理清楚MAMBA专门花时间拜读这篇长达330页的博士论文&#xff0c;由于知识水平有限&#xff0c;只能尽自己所能概述记录&#xff0c;并适…

在微信公众号中加入ChatGPT聊天的方法

1 介绍 开源项目 "chatgpt-on-wechat" 支持通过微信公众号进行调用&#xff0c;这意味着用户可以在与公众号的交互中体验 ChatGPT。由于服务是部署在远端服务器上的&#xff0c;因此用户只需拥有一部手机&#xff0c;就可以在任何环境下与 ChatGPT 进行交流。例如&am…

回归预测 | Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据…

forEach如何跳出或中断循环

文章目录 概要使用try...catch抛出错误设置数组长度为0使用splice删除数组的元素 概要 在Javascript中&#xff0c;我们使用forEach遍历循环的时候&#xff0c;往往会面临跳出循环和中断循环&#xff0c;我们可以采取以下几种方式&#xff1a; 使用try…catch抛出错误 const …

IGBT工作原理

IGBT&#xff08;绝缘栅双极型晶体管) 在实际应用中最流行和最常见的电子元器件是双极结型晶体管 BJT 和 MOS管。 IGBT实物图电路符号图 你可以把 IGBT 看作 BJT 和 MOS 管的融合体&#xff0c;IGBT具有 BJT 的输入特性和 MOS 管的输出特性。 与 BJT 或 MOS管相比&#xff0c;…

手把手教你学会接口自动化系列二十三-引入allure优化我们的代码结构生成报告

优化之后的代码如下&#xff1a; # !/usr/bin/env python# -*- coding: utf-8 -*-# Time : 2024/01# Author : Laopiimport jsonimport loggingimport allurefrom testcase.conftest import *baseUrl GetYamlData(ConfigHandler.config_path).get_yaml_data()root_path os.p…

六、Netty核心模块组件

目录 6.1 BootStrap&#xff0c;ServerBootStrap6.2 Future&#xff0c;ChannelFuture6.3 Channel6.4 Selector6.5 ChannelHandler 以及其实现类6.6 Pipeline 和 ChannelPipeline6.7 ChannelHandlerContext6.8 ChannelOption6.9 EventLoopGroup和其实现类 NioEventLoopGroup6.1…

Forward(转发)和Redirect(重定向)的异同

Forward&#xff08;转发&#xff09;和Redirect&#xff08;重定向&#xff09;是两种不同的服务器端跳转机制&#xff0c;它们在Servlet中用于控制页面的跳转和处理请求的方式。 Forward&#xff08;转发&#xff09;&#xff1a; 特点&#xff1a; 转发是在服务器内部进行的…