Spring Boot + MinIO 实现分段、断点续传,让文件传输更高效

news2025/1/4 8:57:29

一、引言

在当今的互联网应用中,文件上传是一个常见的功能需求。然而,传统的文件上传方式在面对大文件或不稳定的网络环境时,可能会出现性能瓶颈和上传失败的问题。

传统文件上传,就像是用一辆小推车搬运大型家具,一次只能运送一件,不仅耗时久,还容易在途中 “翻车”,导致上传中断。而且,一旦上传失败,就得从头再来,极大地浪费了时间和资源。

为了解决这些问题,分片上传、断点续传技术应运而生。Spring Boot 作为一款流行的 Java 开发框架,提供了简洁高效的开发体验;MinIO 则是一个高性能的对象存储服务器,支持 S3 协议,能够可靠地存储文件。将它们结合使用,可以轻松实现文件的分段、断点续传功能,为用户提供更流畅的上传体验。就好比把大型家具拆分成零部件,用多辆小推车并行运输,即使某一辆车出了问题,后续也能快速从断点处继续,大大提高了运输效率。

二、实现分段上传

(一)原理剖析

        分段上传,又称分片上传,其核心原理是将大文件依照特定大小分割成一系列小块,这些小块被称为分片。随后,各个分片作为独立单元,分别通过网络上传至服务器。以传输一部高清电影为例,若电影文件大小为 5GB,按照 5MB 为一个分片进行切割,可得到 1000 个分片。在上传时,这 1000 个分片能够依次或并行地向服务器发起传输请求。相较于传统的一次性上传整个大文件,分段上传优势显著。一方面,它能极大提高上传的稳定性。在网络环境不稳定,频繁出现丢包、延迟等情况时,一次性传输大文件极易导致上传失败,而分段上传即便某个分片传输受阻,只需重新传输该分片即可,不会影响其他已成功传输的分片,有效降低了整体上传失败的风险。另一方面,分段上传支持并行操作,多个分片可同时进行上传,充分利用网络带宽资源,大大加快了上传速度。尤其在面对网络状况良好的环境,并行上传多个分片能够显著缩短大文件的上传时间,提升用户体验。

(二)MinIo使用

1.Windows安装

下载路径:MinIO下载和安装 | 用于创建高性能对象存储的代码和下载内容

CMD执行 C:\minio.exe server F:\Data --console-address ":9001"

启动后

2.Linux安装

下载地址:MinIO下载 | 中国镜像下载加速站

使用minio方式安装

wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio
chmod +x minio
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001" 

使用rpm方式安装 

wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio-20241218131544.0.0-1.x86_64.rpm

rpm -ivh minio-20241218131544.0.0-1.x86_64.rpm

minio server ./

Systemd配置

.rpm软件包将以下systemd服务文件安装到/usr/lib/systemd/system/minio.service

[Unit]
Description=MinIO
Documentation=https://min.io/docs/minio/linux/index.html
Wants=network-online.target
After=network-online.target
AssertFileIsExecutable=/usr/local/bin/minio

[Service]
WorkingDirectory=/usr/local

User=minio-user
Group=minio-user
ProtectProc=invisible

EnvironmentFile=-/etc/default/minio
ExecStartPre=/bin/bash -c "if [ -z \"${MINIO_VOLUMES}\" ]; then echo \"Variable MINIO_VOLUMES not set in /etc/default/minio\"; exit 1; fi"
ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES

# MinIO RELEASE.2023-05-04T21-44-30Z adds support for Type=notify (https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=)
# This may improve systemctl setups where other services use `After=minio.server`
# Uncomment the line to enable the functionality
# Type=notify

# Let systemd restart this service always
Restart=always

# Specifies the maximum file descriptor number that can be opened by this process
LimitNOFILE=65536

# Specifies the maximum number of threads this process can create
TasksMax=infinity

# Disable timeout logic and wait until process is stopped
TimeoutStopSec=infinity
SendSIGKILL=no

[Install]
WantedBy=multi-user.target

# Built for ${project.name}-${project.version} (${project.name})

默认情况下,minio.service文件作为minio-user用户和组运行。您可以使用groupadduseradd命令创建用户和组。以下示例创建用户和组,并设置访问MinIO要使用的文件夹路径的权限。这些命令通常需要root(sudo)权限。 

groupadd -r minio-user
useradd -M -r -g minio-user minio-user
chown minio-user:minio-user /mnt/data

 创建环境变量文件

/etc/default/minio创建一个环境变量文件。MinIO Server容器可以使用此文件作为所有环境变量的源。

MINIO_OPTS="--address '0.0.0.0:9050' --console-address '0.0.0.0:9051' "
MINIO_VOLUMES="/data/minio/data"

MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=minio123

最后 sudo systemctl start minio.service 启动minio服务

常用命令

启动MinIO

systemctl start minio
查询运行状态

systemctl status minio

停止MinIO

systemctl stop minio

 3.minio配置

登录minio web端  

创建桶

​ 

​ 

​ 

创建Key

(三)核心代码解读

  1. 配置 MinIO 客户端:在 Spring Boot 项目中引入 MinIO 依赖,首先需在 pom.xml 文件中添加相应依赖项:
<dependency>

    <groupId>io.minio</groupId>

    <artifactId>minio</artifactId>

    <version>8.4.4</version>

</dependency>

此处以 8.4.4 版本为例,实际使用时应根据 MinIO 官方发布的稳定版本进行调整。引入依赖后,接着在 application.yml 或 application.properties 配置文件中配置 MinIO 连接信息:

minio:
  endpoint: http://localhost:9000  //minio api地址端口
  access-key: accessKey           //minio access-key
  secret-key: secretKey            //minio secretKey
  bucket-name: your-bucket-name    //桶

上述配置中,endpoint指定 MinIO 服务的访问地址,access-key与secret-key是访问 MinIO 服务器的凭证,用于身份验证,bucket-name则是文件存储的桶名称。通过这些配置,Spring Boot 项目便能顺利与 MinIO 服务器建立连接,为后续文件上传操作奠定基础。

  1. 文件分片上传逻辑:以下是一段基于 Spring Boot 结合 MinIO 实现文件分片上传的关键代码片段:
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

@RestController
public class FileUploadController {

    @Autowired
    private MinioClient minioClient;

    // 定义分片大小,这里设为5MB,可根据实际情况调整
    private static final long CHUNK_SIZE = 5 * 1024 * 1024; 

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
        String bucketName = "your-bucket-name";
        String objectName = file.getOriginalFilename();
        InputStream inputStream = file.getInputStream();
        long fileSize = file.getSize();

        // 计算分片数量
        long totalChunks = fileSize % CHUNK_SIZE == 0? fileSize / CHUNK_SIZE : fileSize / CHUNK_SIZE + 1;

        for (int i = 0; i < totalChunks; i++) {
            long offset = i * CHUNK_SIZE;
            long chunkSize = Math.min(CHUNK_SIZE, fileSize - offset);
            byte[] buffer = new byte[(int) chunkSize];
            inputStream.read(buffer);

            // 构建上传参数,上传每个分片
            PutObjectArgs args = PutObjectArgs.builder()
                   .bucket(bucketName)
                   .object(objectName + "_chunk_" + i)
                   .stream(new ByteArrayInputStream(buffer), chunkSize, -1)
                   .build();
            try {
                minioClient.putObject(args);
            } catch (Exception e) {
                // 处理上传分片失败的情况,可记录日志、重试等
                e.printStackTrace();
            }
        }

        return "File uploaded successfully";
    }
}

        在这段代码中,首先通过@Autowired注解注入MinioClient,确保能够与 MinIO 服务器进行交互。接着定义了分片大小CHUNK_SIZE,这里设定为 5MB,开发者可依据文件特性、网络带宽等因素灵活调整。在uploadFile方法中,获取上传的文件信息,包括文件名、输入流以及文件大小,并根据分片大小计算出分片总数。随后,通过循环依次读取每个分片的数据,利用MinioClient的putObject方法将分片上传至指定的存储桶中。若上传过程中某个分片出现异常,代码中简单地进行了打印堆栈信息的处理,实际应用中,可根据需求进一步优化,如记录详细日志、尝试自动重试上传等,以增强系统的可靠性与稳定性。

四、实现断点续传

(一)原理剖析

        断点续传是在分片上传基础上的一项关键优化,其核心原理在于精准记录上传进度,并在遭遇网络或操作中断后,能够从断点处继续上传。当文件开始分片上传时,系统会同步记录已成功上传的分片信息,常见的记录方式包括在数据库中存储已上传分片的序号、使用 Redis 等缓存工具记录上传状态等。一旦上传过程中断,无论是由于网络故障、设备断电还是用户主动暂停,下次上传时,系统首先会查询记录的上传进度,识别出已上传的分片,随后仅传输尚未完成的分片,避免了重复劳动。这种机制极大地提升了用户体验,特别是在上传大文件时,若因意外中断而需从头开始,将耗费大量时间与资源,而断点续传则有效解决了这一痛点,让文件上传更加智能、高效。

(二)核心代码解读

  1. 记录上传进度:以下是一段使用 Redis 记录上传进度的示例代码:
import redis.clients.jedis.Jedis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class UploadProgressRecorder {

    @Autowired
    private Jedis jedis;

    public void recordProgress(String uploadId, int chunkIndex) {
        jedis.sadd(uploadId, String.valueOf(chunkIndex));
    }

    public boolean isChunkUploaded(String uploadId, int chunkIndex) {
        return jedis.sismember(uploadId, String.valueOf(chunkIndex));
    }

    public void close() {
        jedis.close();
    }
}

在这段代码中,UploadProgressRecorder类负责与 Redis 交互,记录和查询上传进度。通过@Autowired注入Jedis实例,recordProgress方法使用SADD命令将已上传的分片索引添加到以uploadId为键的集合中,isChunkUploaded方法则借助SISMEMBER命令判断指定分片是否已上传,最后提供close方法关闭 Redis 连接,确保资源的正确释放。这里使用集合数据结构来存储已上传分片索引,能够方便地进行成员判断与批量操作,适用于频繁的上传进度记录与查询场景。

  1. 续传逻辑实现:在文件上传控制器中,结合上述记录进度的功能,实现断点续传逻辑:
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Set;

@RestController
public class FileUploadController {

    @Autowired
    private MinioClient minioClient;
    @Autowired
    private UploadProgressRecorder progressRecorder;

    // 定义分片大小,这里设为5MB,可根据实际情况调整
    private static final long CHUNK_SIZE = 5 * 1024 * 1024; 

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
        String bucketName = "your-bucket-name";
        String objectName = file.getOriginalFilename();
        String uploadId = generateUploadId(); // 生成唯一的上传ID,用于标识本次上传任务

        long fileSize = file.getSize();
        long totalChunks = fileSize % CHUNK_SIZE == 0? fileSize / CHUNK_SIZE : fileSize / CHUNK_SIZE + 1;

        // 检查已上传的分片
        Set<String> uploadedChunks = progressRecorder.getUploadedChunks(uploadId);

        for (int i = 0; i < totalChunks; i++) {
            if (!uploadedChunks.contains(String.valueOf(i))) {
                long offset = i * CHUNK_SIZE;
                long chunkSize = Math.min(CHUNK_SIZE, fileSize - offset);
                byte[] buffer = new byte[(int) chunkSize];
                file.getInputStream().read(buffer);

                // 构建上传参数,上传未完成的分片
                PutObjectArgs args = PutObjectArgs.builder()
                      .bucket(bucketName)
                      .object(objectName + "_chunk_" + i)
                      .stream(new ByteArrayInputStream(buffer), chunkSize, -1)
                      .build();
                try {
                    minioClient.putObject(args);
                    progressRecorder.recordProgress(uploadId, i); // 记录分片上传成功
                } catch (Exception e) {
                    // 处理上传分片失败的情况,可记录日志、重试等
                    e.printStackTrace();
                }
            }
        }

        // 判断所有分片是否都已上传完成,若完成则可进行合并操作或其他后续处理
        if (uploadedChunks.size() == totalChunks) {
            // 执行合并分片等操作
            return "File uploaded and merged successfully";
        }

        return "Upload paused, will resume from breakpoint next time";
    }
}

在上述uploadFile方法中,首先生成一个唯一的上传 ID,用于在后续操作中准确标识本次上传任务。接着,依据文件大小与预设的分片大小计算出总分片数。随后,通过progressRecorder查询已上传的分片集合,在循环上传分片时,跳过已存在于集合中的分片,仅上传尚未完成的部分,并在每次成功上传后,及时记录该分片的上传状态。最后,通过对比已上传分片数量与总分片数,判断文件是否全部上传完毕,若完成则可触发后续的合并操作或进行相应的业务处理,若未完成,则告知用户上传处于暂停状态,下次将从断点处继续。如此一来,系统便能在面对复杂多变的网络环境与用户操作时,稳定、高效地实现文件的断点续传功能。

五、优化与拓展

在实现了基本的分段、断点续传功能后,还可以从多个方面对文件上传系统进行优化与拓展,进一步提升性能与用户体验。

(一)上传性能优化

  1. 调整分片大小:分片大小的选择对上传性能有着显著影响。若分片过大,在网络不稳定时,单个分片传输失败的概率会增加,且重传成本高;若分片过小,会产生过多的网络请求,增加服务器的处理开销与延迟。一般而言,可依据网络带宽的稳定程度、服务器的处理能力以及文件类型来动态调整分片大小。例如,在网络带宽充裕且稳定时,适当增大分片大小,以减少请求次数,提高传输效率;反之,在网络波动较大时,减小分片大小,降低单个分片传输失败的风险。通过实时监测网络状况,结合文件大小,智能地选择最优分片大小,能够极大提升上传的稳定性与速度。
  1. 优化网络请求:在文件上传过程中,减少不必要的网络请求开销至关重要。一方面,可采用连接池技术,复用已建立的网络连接,避免频繁地创建与销毁连接带来的性能损耗。例如,在 Spring Boot 项目中配置合适的连接池参数,如最大连接数、最小空闲连接数等,确保在高并发上传场景下,网络连接资源能够得到高效利用。另一方面,对网络请求进行批量处理,将多个小的上传请求合并为一个大的请求,减少请求头、响应头等额外信息的传输开销,提高网络传输的有效负载率。同时,结合异步编程模型,让文件上传操作在后台线程执行,避免阻塞主线程,提升系统的响应性,使得用户在上传文件时,仍能流畅地进行其他操作。

(二)功能拓展思路

  1. 结合秒传功能:秒传功能是提升用户上传体验的一大利器。其原理是在上传文件之前,先计算文件的哈希值(如常用的 MD5、SHA-256 等算法),然后将该哈希值发送至服务器。服务器依据此哈希值在存储系统中快速检索,判断是否已存在相同哈希值的文件。若存在,则直接返回该文件的访问链接,用户无需重复上传实际的文件内容,瞬间完成 “上传” 操作。这对于一些常见的、已在服务器存储过的文件,如常用的图片、文档模板等,能够极大地节省上传时间与带宽资源。在结合 Spring Boot 与 MinIO 实现时,可在文件上传控制器中新增一个接口,用于接收文件哈希值并进行秒传判断。若文件不存在,则按照常规的分段、断点续传流程进行上传,从而实现秒传与分段上传的无缝融合。
  1. 增加文件校验:为确保上传文件的完整性与准确性,文件校验必不可少。除了在断点续传中记录已上传分片信息,防止重复上传外,还可在上传完成后,对整个文件进行校验。一种常见的方式是在客户端计算文件的哈希值,并在上传完成后,将该哈希值与服务器端重新计算的文件哈希值进行比对。若两者一致,则表明文件在传输过程中未发生损坏或篡改;若不一致,则提示用户文件可能存在问题,需要重新上传。此外,还可引入数据冗余技术,如在 MinIO 存储中采用纠删码(Erasure Coding),即使部分数据丢失或损坏,仍能通过冗余信息恢复文件,进一步提高数据的可靠性,为用户提供更加安全、可靠的文件上传服务。

七、总结

通过 Spring Boot 与 MinIO 的强强联合,我们成功实现了文件的分段、断点续传功能,有效解决了大文件上传在不稳定网络环境下的难题。这一技术方案不仅提升了文件上传的稳定性与效率,还为用户带来了更加流畅的使用体验。在实际应用中,开发者可根据具体业务场景,灵活调整分片大小、优化网络请求,进一步拓展秒传、文件校验等功能,满足多样化的需求。未来,随着云计算与大数据技术的不断发展,相信这一技术组合将在更多领域发挥重要作用,如视频直播、云存储服务、大数据分析等,为海量数据的高效传输与处理提供坚实保障,助力企业实现数字化转型与升级。

2024年的最后一篇文章 在这祝大家新年快乐,一夜暴富!!!

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

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

相关文章

搭建android开发环境 android studio

1、环境介绍 在进行安卓开发时&#xff0c;需要掌握java&#xff0c;需要安卓SDK&#xff0c;需要一款编辑器&#xff0c;还需要软件的测试环境&#xff08;真机或虚拟机&#xff09;。 早起开发安卓app&#xff0c;使用的是eclipse加安卓SDK&#xff0c;需要自行搭建。 目前开…

开发过程优化·自定义鼠标右键菜单

为了改善日常工作中的开发体验&#xff0c;我希望在某个项目目录下点击鼠标右键的快捷菜单&#xff0c;让程序自动为该项目引入一个内部的工具库文件并挂载到项目中。 实现该功能需要组装一些零碎的电脑应用知识&#xff0c;下面徐徐渐进依次说明&#xff1a; 1、在右键菜单中…

搭建ZooKeeper分布式集群

ZooKeeper分布式集群部署旨在通过多节点协作实现高可用性和容错能力。本次实战以三台服务器&#xff08;master、slave1、slave2&#xff09;为例&#xff0c;详细介绍了从下载安装到配置启动的全过程。首先&#xff0c;下载并解压ZooKeeper安装包至/usr/local目录&#xff0c;…

elasticsearch-java客户端jar包中各模块的应用梳理

最近使用elasticsearch-java客户端实现对elasticsearch服务的Api请求&#xff0c;现对elasticsearch-java客户端jar包中各模块的应用做个梳理。主要是对co.elastic.clients.elasticsearch路径下的各子包的简单说明。使用的版本为&#xff1a;co.elastic.clients:elasticsearch-…

【AIGC】使用Java实现Azure语音服务批量转录功能:完整指南

文章目录 引言技术背景环境准备详细实现1. 基础架构设计2. 实现文件上传功能3. 提交转录任务crul4. 获取转录结果 使用示例结果示例最佳实践与注意事项总结 引言 在当今数字化时代&#xff0c;将音频内容转换为文本的需求越来越普遍。无论是会议记录、视频字幕生成&#xff0c…

InstructGPT:基于人类反馈训练语言模型遵从指令的能力

大家读完觉得有意义记得关注和点赞&#xff01;&#xff01;&#xff01; 大模型进化树&#xff0c;可以看到 InstructGPT 所处的年代和位置。来自 大语言模型&#xff08;LLM&#xff09;综述与实用指南&#xff08;Amazon&#xff0c;2023&#xff09; 目录 摘要 1 引言 …

kafka开机自启失败问题处理

前言&#xff1a;在当今大数据处理领域&#xff0c;Kafka 作为一款高性能、分布式的消息队列系统&#xff0c;发挥着举足轻重的作用。无论是海量数据的实时传输&#xff0c;还是复杂系统间的解耦通信&#xff0c;Kafka 都能轻松应对。然而&#xff0c;在实际部署和运维 Kafka 的…

在Linux上获取MS(如Media Server)中的RTP流并录制为双轨PCM格式的WAV文件

在Linux上获取MS(如Media Server)中的RTP流并录制为双轨PCM格式的WAV文件 一、RTP流与WAV文件格式二、实现步骤三、伪代码示例四、C语言示例代码五、关键点说明六、总结在Linux操作系统上,从媒体服务器(如Media Server,简称MS)获取RTP(Real-time Transport Protocol)流…

蓝桥杯(Java)(ing)

Java前置知识 输入流&#xff1a; &#xff08;在Java面向对象编程-CSDN博客里面有提过相关知识------IO流&#xff09; // 快读快写 static BufferedReader in new BufferedReader(new InputStreamReader(System.in)); static BufferedWriter out new BufferedWriter(new…

python钉钉机器人

上代码 #coding:utf-8 import sys import time import hmac import hashlib import base64 import urllib.parse import requeststimestamp str(round(time.time() * 1000)) secret 你的secret secret_enc secret.encode(utf-8) string_to_sign {}\n{}.format(timestamp, …

「Mac畅玩鸿蒙与硬件50」UI互动应用篇27 - 水果掉落小游戏

本篇教程将带你实现一个水果掉落小游戏&#xff0c;掌握基本的动态交互逻辑和鸿蒙组件的使用&#xff0c;进一步了解事件处理与状态管理。 关键词 UI互动应用水果掉落状态管理动态交互游戏开发 一、功能说明 水果掉落小游戏包含以下交互功能&#xff1a; 随机生成水果&#…

用 vue3 实现新年快乐

提前祝福大家新年快乐&#xff0c;今天用一个新年快乐的教程来结束这一年。 看下效果 在这个案例中&#xff0c;我们使用了 vue3 &#xff0c;有一个浮动的新年快乐的字体&#xff0c;然后有一堆从下到上的小粒子&#xff0c;在文字背后有一个模拟烟花绽放的效果。 环境搭建…

QT集成IntelRealSense双目摄像头2,集成OpenGL

上一篇文章写了如何把IntelRealSense摄像头的SDK集成到QT项目&#xff0c;并成功采集数据&#xff0c;在没有用OpenCV的情况下完成色彩数据&#xff0c;以及深度数据的显示。 具体地址&#xff1a;https://blog.csdn.net/qujia121qu/article/details/144734163 本次主要写如何…

【模块一】kubernetes容器编排进阶实战之kubernetes pod Affinity与pod antiaffinity

pod Affinity与pod antiaffinity Pod Affinity与anti-affinity简介&#xff1a; Pod亲和性与反亲和性可以基于已经在node节点上运行的Pod的标签来约束新创建的Pod可以调度到的 目的节点&#xff0c;注意不是基于node上的标签而是使用的已经运行在node上的pod标签匹配。 其规则…

从百度云网盘下载数据到矩池云网盘或者服务器内

本教程教大家如何快速将百度云网盘数据集或者模型代码文件下载到矩池云网盘或者服务器硬盘上。 本教程使用到了一个开源工具 BaiduPCS-Go&#xff0c;官方地址 &#xff1a; https://github.com/qjfoidnh/BaiduPCS-Go 这个工具可以实现“仿 Linux shell 文件处理命令的百度网…

使用maven-mvnd替换maven大大提升编译打包速度

先上结论&#xff01;&#xff01;&#xff01; 多模块清理并打包提升&#xff1a;约3.5倍 多模块不清理打包提升&#xff1a;约5.5倍 单模块提升&#xff1a;约2倍 从计算结果来看&#xff0c;多模块提升的效率更高。在使用mvnd package打包多模块式&#xff0c;可在控制台…

LeetCode - 初级算法 数组(删除排序数组中的重复项)

免责声明:本文来源于个人知识与公开资料,仅用于学术交流。 删除排序数组中的重复项 这篇文章讨论如何从一个非严格递增的数组 nums 中删除重复的元素,使每个元素只出现一次,并返回新数组的长度。因为数组是排序的,只要是相同的肯定是挨着的,所以我们需要遍历所有数组,然…

2024 年度总结

时光荏苒&#xff0c;2024 年即将画上句号&#xff0c;回顾这一年的写博历程&#xff0c;有付出、有收获、有成长&#xff0c;也有诸多值得回味与反思的瞬间。 一、内容创作 主题涉猎&#xff1a;这一年&#xff0c;我致力于探索多样化的主题&#xff0c;以满足不同读者群体的…

基于STM32位单片机的腕式运动体力监测装置设计

本设计基于STM32位单片机的腕式运动体力状态诊断系统装置。本系统内的使用的STM32单片机包含了心率检测电路、呼吸频率检测电路、OLED液晶显示电路、电源电路、蓝牙电路。通过心率传感器以及手指脉搏波动放大过后发送给比较器&#xff0c;结果处理后发送给单片机进行信息的收集…

LunarVim安装

LunarVim以其丰富的功能和灵活的定制性&#xff0c;迅速在Nvim用户中流行开来。它不仅提供了一套完善的默认配置&#xff0c;还允许用户根据自己的需求进行深度定制。无论是自动补全、内置终端、文件浏览器&#xff0c;还是模糊查找、LSP支持、代码检测、格式化和调试&#xff…