大文件上传服务-后端V1V2
文章目录
- 大文件上传概述:
- minio分布式文件存储
- 使用的一些技术
- 校验MD5的逻辑
- uploadV1 版本 1
- uploadv2 版本 2
大文件上传概述:
之前项目做了一个文件上传的功能,最近看到有面试会具体的问这个上传功能的细节,把之前做的项目拿过来总结一下,自己写的一个文件上传服务,如果是单体项目直接就是 file-model,微服务可以给文件单独搞一个服务。
对于大的文件上传由于网络带宽的限制非常的耗时间,可以将文件进行切分,类似于IP数据包重组和分片。
类似于百度网盘一秒上传文件,这个如何实现的呢? 相同的文件上传过一次之后使用了MD5加密的算法,下次就不用等待上传了。
对于海量的文件,如何去存储呢? 这些都是需要去考虑的内容。可以使用轻量级的分布式存储minio来存储文件。复杂一点可以使用 fastDFS 来存储文件。
文件存储 sql 表:
create table file_url_table
(id int auto_incrementprimary key,file_name varchar(255) not null,file_type varchar(30) not null,file_md5 varchar(32) not null,file_url varchar(512) not null,endpoint varchar(255) not null,buck_name varchar(255) not null,object_name varchar(255) not null,created_at timestamp default CURRENT_TIMESTAMP null,updated_at timestamp default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP
);
minio分布式文件存储
后端文件的存储使用分布式文件存储系统minio
一个minio的地址包括:
endpoint:节点的信息 http:ip地址+:端口号
buckName: 桶的基础信息
objectName: 文件夹的信息+文件的名称。
http://xxxxx:9000/powerproject/file/2024/12/01/bbb5ba51d7b65cf4495de91ca55bfa23.mp3
项目中使用 minio
导入minio maven依赖:
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.7</version>
</dependency>
配置minoConfig,使用Configuration注解,spring在启动的时候会扫描这个类。@bean会创建一个MinioClient,交给Spring容器去管理。
@Configurationpublic class MinioConfig {@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Value("${minio.endpoint}")private String endPoint;@Beanpublic MinioClient minioClient() {return MinioClient.builder().endpoint(endPoint).credentials(accessKey, secretKey).build();}
}
**@Bean**
注解定义了一个 Spring Bean 方法,用来初始化并提供配置好的MinioClient
。- 通过自动注入,Spring Boot 会将配置文件中的
minio.accessKey
,minio.secretKey
,minio.endPoint
自动注入到类的字段中,并将配置好的MinioClient
实例作为 Bean 提供给整个应用使用。 - spring启动的时候会扫描到configuration这个包。
断点续传:
通常视频文件都比较大,所以对于媒资系统上传文件的需求要满足大文件的上传要求。http协议本身对上传文件大小没有限制,但是客户的网络环境质量、电脑硬件环境等参差不齐,如果一个大文件快上传完了网断了没有上传完成,需要客户重新上传,用户体验非常差,所以对于大文件上传的要求最基本的是断点续传。
什么是断点续传:
引用百度百科:断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载,断点续传可以提高节省操作时间,提高用户体验性。
使用的一些技术
因为上传的结构传进来的都是MultipartFile,但是文件合并完成之后是File类型的,需要将File转为MultipartFile。
//将木桶的文件 转换成 MultipartFileMultipartFile multipartFile = new MockMultipartFile("file", // 表单中文件参数的名字fileName, // 文件名fileType, // 文件类型new FileInputStream(outputFile) // 文件输入流);
MockMultipartFile
的构造函数有四个参数:
- 第一个参数:
"file"
- 这是表单中用于接收文件的字段名。通常是前端表单中的
<input type="file" name="file">
中的name
属性值。 - 例如,在 HTML 表单中,字段可能长这样:
- 这是表单中用于接收文件的字段名。通常是前端表单中的
<input type="file" name="file">
- 后端通过 `"file"` 来访问上传的文件。
- 第二个参数:
fileName
- 这是文件的名字,即上传文件时的文件名。它将作为文件在服务器上存储时的名称,或者用作处理文件时的标识符。
- 例如,
fileName
可能是"document.pdf"
或"image.jpg"
等。
- 第三个参数:
fileType
- 这是文件的 MIME 类型(文件类型)。常见的文件类型如
"application/pdf"
,"image/jpeg"
,"text/plain"
等。 - 在这里,它指定了文件的格式/类型,告诉服务器如何处理该文件。
- 这是文件的 MIME 类型(文件类型)。常见的文件类型如
- 第四个参数:
new FileInputStream(outputFile)
- 这是文件内容的输入流。
**FileInputStream**
** 用于读取文件内容并将其传递给**MockMultipartFile**
。** outputFile
是一个File
对象,表示要上传的实际文件。通过new FileInputStream(outputFile)
,你可以将文件内容转换为字节流,使得它可以被传递到MockMultipartFile
中。
- 这是文件内容的输入流。
MultipartFile
提供了几个常用方法,比如:
getName()
: 获取文件的字段名。getOriginalFilename()
: 获取文件的原始文件名。getBytes()
: 获取文件的字节内容。getInputStream()
: 获取文件的输入流。
校验MD5的逻辑
对于已经存在的文件,文件的 md5 值都是相同的。文件上传前端解析 md5,查询数据库中是否有数据,如果没有再实现文件上传。
//去数据库中查询这个文件的哈希值是否存在,如果存在就不用上传了,直接拿到这个文件的地址
@GetMapping("/md5")
public AjaxResult getMd5(@RequestParam("md5") String md5) {Map<String, Object> map=fileService.getMd5(md5);return AjaxResult.success(map);
}
@SneakyThrows
@Override
public Map<String, Object> getMd5(String md5) {
// 从数据库查询文件信息
FileEntity file = fileMapper.getMd5(md5);// 如果文件不存在,直接返回 null
if (file == null) {return null;
}// 获取文件相关信息
String bucketName = file.getBuckName();
String objectName = file.getObjectName();
String fileUrl = file.getFileUrl();
String fileType = file.getFileType();
String fileName = file.getFileName();try {// 检查文件是否存在于 MinIOif (!minioTemplate.isExist(objectName)) {// 文件不存在return null;}
} catch (MinioException e) {// 如果 MinIO 抛出异常,记录异常信息return null;
}// 文件存在,构建返回的 Map
Map<String, Object> result = new HashMap<>();
result.put("url", fileUrl);
result.put("fileType", fileType);
result.put("fileName", fileName);return result;
}
判断文件是否存在
public boolean isExist(String objectName) throws Exception {StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());return statObjectResponse == null;}
uploadV1 版本 1
上传整个文件。
@GetMapping("/uploadV1")public AjaxResult uploadFileV1(MultipartFile file) {Map<String, Object> map = fileService.uploadV1(file);return AjaxResult.success(map);}
前置校验,检查文件的大小。 文件不能过大。 限制了一些文件上传的大小 FileType 枚举类
// 通过枚举类校验文件的大小
private void preCheck(MultipartFile file) {
String fileName = file.getOriginalFilename(); //得到原来的名称
String fileSuffix = fileName.substring(fileName.lastIndexOf(".")); // 拿到后缀
FileType fileType = FileType.getFileTypeByExtension(fileSuffix);
if (fileType == null) {throw new ServiceException("不支持的文件格式");
}// 校验文件大小
if (file.getSize() > fileType.getMaxSize()) {throw new ServiceException("文件大小超过" + (fileType.getMaxSize() / (1024 * 1024)) + "MB");
}
}
import java.util.Arrays;
import java.util.List;public enum FileType {//两个属性 一个是文件的扩展名 一个是文件大小IMAGE(Arrays.asList(".jpg", ".png", ".jpeg"), 1024 * 1024 * 50), // 50MBDOCUMENT(Arrays.asList(".doc", ".xls", ".ppt", ".txt", ".pdf", ".mp3", ".wav", ".ply"), 1024 * 1024 * 100), // 100MBVIDEO(Arrays.asList(".mp4", ".avi", ".mov"), 1024 * 1024 * 200); // 200MBprivate List<String> extensions; // 支持的文件扩展名private long maxSize; // 最大文件大小// 构造函数FileType(List<String> extensions, long maxSize) {this.extensions = extensions;this.maxSize = maxSize;}public List<String> getExtensions() {return extensions;}public long getMaxSize() {return maxSize;}// 根据文件扩展名获取文件类型public static FileType getFileTypeByExtension(String fileSuffix) {for (FileType type : FileType.values()) {if (type.getExtensions().contains(fileSuffix)) {return type;}}return null; // 如果没有匹配的文件类型,返回 null}
}
不同的文件放在不同的文件夹下面。
image开头的 存储一些 .jpg、.jped,.png文件。
file 开头的存放 .doc,.xls文件。 使用枚举类可以来实现动态的管理。 设置文件存储路径,使用日期的存储不同,不用的文件类型存储不同的路径
String fileBucket = getFileBucket(fileSuffix);if (fileBucket == null) {return null;}// 获取当前日期Date now = new Date();// 使用 SimpleDateFormat 来格式化日期为 "yyyy/MM/dd" 格式DateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");String date = sdf.format(now);// 组合最终的文件夹路径:如 "image/2024/11/30/"return fileBucket + date + "/";}private static String getFileBucket(String fileSuffix) {return FileCategory.getBucketPrefixByExtension(fileSuffix);}
package com.njitzx.fileupload.enu;import java.util.Arrays;
import java.util.List;public enum FileCategory {IMAGE(Arrays.asList(".jpg", ".jpeg", ".png", ".gif"), "image/"),DOCUMENT(Arrays.asList(".doc", ".xls", ".ppt", ".txt", ".pdf", ".mp3", ".wav", ".ply"), "file/"),VIDEO(Arrays.asList(".mp4", ".avi", ".mov"), "video/"),AUDIO(Arrays.asList(".mp3", ".wav"), "audio/");private List<String> extensions; // 支持的文件扩展名private String bucketPrefix; // 存储路径前缀// 构造函数FileCategory(List<String> extensions, String bucketPrefix) {this.extensions = extensions;this.bucketPrefix = bucketPrefix;}// 获取支持的文件扩展名列表public List<String> getExtensions() {return extensions;}// 获取对应的存储路径public String getBucketPrefix() {return bucketPrefix;}// 根据文件扩展名获取对应的存储路径public static String getBucketPrefixByExtension(String fileSuffix) {for (FileCategory category : FileCategory.values()) {if (category.getExtensions().contains(fileSuffix)) {return category.getBucketPrefix();}}return null; // 如果没有找到对应的类型,返回 null}
}
上传的Service代码。
文件上传成功后需要将信息插入到文件表中去。 文件存储 service 方法:
public Map<String, Object> uploadV1(MultipartFile file) {
//文件的名称
String md5Name = MD5Util.getFileMD5(file);String fileName = file.getOriginalFilename();String fileSuffix = fileName.substring(fileName.lastIndexOf("."));String folderName = generateFolderName(fileSuffix); //文件夹名称
//拼接objectName
String objectName = folderName + md5Name + fileSuffix; //objectName//拼接文件的全文路径
String fileUrl = endPoint + "/" + bucketName + "/" + objectName; //完整的URL//放入到minio中去
minioTemplate.pubObject(objectName, file); //存入到minio中去//将文件上传信息插入到数据库中
FileEntity file1 = FileEntity.builder().fileName(fileName).fileType(fileSuffix.substring(1))
.fileUrl(fileUrl).endpoint(endPoint).buckName(bucketName).objectName(objectName).createdAt(new Date()).updatedAt(new Date()).fileMd5(md5Name).build();
//插入文件信息
HashMap<String, Object> map = new HashMap<>();
fileMapper.insertFile(file1);//存储map中返回给前端 文件url、name、文件类型
map.put("fileUrl", fileUrl);
map.put("fileName", fileName);
map.put("fileType", fileSuffix.substring(1));return map;
}
mapper的插入语句
<!-- 插入文件记录 --><insert id="insertFile" parameterType="com.njitzx.fileupload.pojo.FileEntity">INSERT INTO file_url_table (file_name, file_type, file_md5, file_url, endpoint, buck_name, object_name,created_at, updated_at)VALUES (#{fileName}, #{fileType}, #{fileMd5}, #{fileUrl}, #{endpoint}, #{buckName}, #{objectName}, #{createdAt},#{updatedAt})</insert>
测试接口:
uploadv2 版本 2
前端上传的大文件 在前端进行切片传输,后端接收切片的文件,整合组合成一个新的文件。
涉及到切片,类似于IP数据包重组,标识一个文件的信息。
fileId 文件信息的唯一标识
currentChunk 当前的块数
totalChunk 总的块数
File 文件的数据
类似于 IP 数据包,切片的那个字段需要有当前第一块,后面是否还有数据包信息。
@PostMapping("/uploadV2")public AjaxResult uploadFileV2(@RequestParam("fileID") Long fileID, // 接收文件ID@RequestParam("currentChunk") Integer currentChunk, // 接收当前块@RequestParam("totalChunk") Integer totalChunk, // 接收总块数@RequestParam("file") MultipartFile file // 接收文件数据) throws IOException {fileService.uploadV2(fileID, currentChunk, totalChunk, file);return AjaxResult.success();}
通过System.getProperty(“user.dir”) 拿到当前的项目目录。
在当前目录下面创建一个upload文件夹,通过fIleId来区分不同的文件。
文件的名称就是当前的 chunk_+当前的块名称。 将传过来的小文件保存起来。
@SneakyThrows
@Override
public void uploadV2(Long fileID, Integer currentChunk, Integer totalChunk, MultipartFile file) {//当前的文件夹String currentDir = System.getProperty("user.dir");String tempDir = currentDir + "/upload/" + fileID;File dir = new File(tempDir);if (!dir.exists()) {dir.mkdirs();}// 保存当前分片String chunkPath = tempDir + "/chunk_" + currentChunk;File chunkFile = new File(chunkPath);file.transferTo(chunkFile);// 记录当前上传分片,等待后续合并System.out.println("保存分片:" + chunkPath);// 如果所有分片上传完毕,返回合并请求
}
前端在所有的分片上传成功之后进行合并请求。
通过get请求 传过来 文件的id、文件名称、文件类型等。
@GetMapping("/merge")
public AjaxResult mergeFile(@RequestParam("fileID") Long fileID, @RequestParam("fileType") String fileType,@RequestParam("fileName") String fileName) throws IOException {Map<String, Object> map = fileService.merge(fileID, fileType, fileName);return AjaxResult.success();
}
合并的步骤是这样的,首先获取到所有的分片文件。按照分片编号进行排序。将所有的小的文件按照顺序组成一个大的文件,这就又回到了第一步,使用uploadv1将文件重组上传。
@SneakyThrows
@Override
public Map<String, Object> merge(Long fileID, String fileType, String fileName) {String currentDir = System.getProperty("user.dir"); //拿到当前项目的目录String type = fileType.substring(fileType.lastIndexOf("/") + 1);String tempDir = currentDir + "/upload/" + fileID;File dir = new File(tempDir);if (!dir.exists()) {throw new ServiceException("分片文件不存在");}// 获取所有的分片文件List<File> chunkFiles = Arrays.asList(Objects.requireNonNull(dir.listFiles()));// 按照分片编号排序chunkFiles.sort(Comparator.comparingInt(file -> Integer.parseInt(file.getName().split("_")[1])));// 合并所有分片文件File outputFile = new File(currentDir + "/upload/" + fileID + "." + type);//true标识不覆盖文件,在原有的文件后面添加。try (FileOutputStream outputStream = new FileOutputStream(outputFile, true)) {//遍历所有的小的文件for (File chunk : chunkFiles) {try (FileInputStream inputStream = new FileInputStream(chunk)) {byte[] buffer = new byte[1024];int len;while ((len = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);}}}}//将木桶的文件 转换成 MultipartFileMultipartFile multipartFile = new MockMultipartFile("file", // 表单中文件参数的名字fileName, // 文件名fileType, // 文件类型new FileInputStream(outputFile) // 文件输入流);// 调用 fileService.uploadV1 方法上传文件Map<String, Object> map = uploadV1(multipartFile);// 返回合并后的文件URLreturn map;
}
上面的内容还是不够完整,因为文件临时存储在本地上,最后合并起来再放入到mino中,还是将大的文件存放到服务器上,如果网络中断并且时间也是较长的。应该将切片的文件存放到minio中去,最后在minio中进行文件的合并。
文件切片上传成功:
相关文章:
大文件上传服务-后端V1V2
文章目录 大文件上传概述:minio分布式文件存储使用的一些技术校验MD5的逻辑 uploadV1 版本 1uploadv2 版本 2 大文件上传概述: 之前项目做了一个文件上传的功能,最近看到有面试会具体的问这个上传功能的细节,把之前做的项目拿过来总结一下,自己写的一个…...
docker 基础语法学习,K8s基础语法学习,零基础学习
下面是关于Docker和Kubernetes的基础语法学习资料,包括一些关键概念和示例代码。 Docker 基础语法 1. 安装 Docker 首先,你需要安装 Docker。以下是不同操作系统上的安装指南: Windows/Mac: 下载并安装 Docker Desktop。 Linux: 根据你的…...
【网络协议】RFC3164-The BSD syslog Protocol
引言 Syslog常被称为系统日志或系统记录,是一种标准化的协议,用于网络设备、服务器和应用程序向中央Syslog服务器发送日志消息。互联网工程任务组(IETF)发布的RFC 3164,专门定义了BSD Syslog协议的规范和实现方式。通…...
MongoDB深度解析与实践案例
MongoDB深度解析与实践案例 在当今大数据与云计算盛行的时代,NoSQL数据库以其灵活的数据模型、水平扩展能力和高性能,成为处理海量数据的重要工具之一。MongoDB,作为NoSQL数据库的杰出代表,凭借其面向文档的存储结构、强大的查询语言以及丰富的生态系统,赢得了众多开发者…...
C语言从零到精通:常用运算符完全指南,掌握算术、逻辑与关系运算
系列文章目录 01-C语言从零到精通:常用运算符完全指南,掌握算术、逻辑与关系运算 文章目录 系列文章目录前言一、C语言的起源与应用领域1.1 C语言的起源1.2 C语言的应用领域1.2.1 操作系统开发1.2.2 嵌入式系统1.2.3 编译器开发1.2.4 游戏开发与图形处理…...
ArkUI概述
鸿蒙操作系统(HarmonyOS)是华为公司推出的一款面向未来、面向全场景的分布式操作系统。它不仅能够支持各种不同的设备,从手机、平板到智能穿戴和智能家居产品,而且为开发者提供了一套统一的开发环境和工具链。对于想要深入鸿蒙开发…...
浅谈计算机网络03 | 现代网络组成
现代网络组成 一 、网络生态体系1.1网络生态系统的多元主体1.2 网络接入设施的多样类型 二、现代网络的典型体系结构解析三、高速网络技术3.1 以太网技术3.2 Wi-Fi技术的深度剖析3.2.1 应用场景的多元覆盖3.2.2 标准升级与性能提升 3.3 4G/5G蜂窝网的技术演进3.3.1 蜂窝技术的代…...
在线图片马赛克处理工具
在线图片马赛克处理工具,无需登录,无需费用,用完就走。 包括中文和英文版本 官网地址: https://mosaic.openai2025.com...
文件上传 分片上传
分片上传则是将一个大文件分割成多个小块分别上传,最后再由服务器合并成完整的文件。这种做法的好处是可以并行处理多个小文件,提高上传效率;同时,如果某一部分上传失败,只需要重传这一部分,不影响其他部分…...
网络安全---CMS指纹信息实战
CMS简介 CMS(Content Management System)指的是内容管理系统,如WordPress、Joomla等。CMS系统非常常见,几乎所有大型网站都使用CMS来管理其网站的内容。由于常见CMS的漏洞较多,因此黑客将不断尝试利用这些漏洞攻击CMS…...
Ubuntu 24.04 LTS 系统语言英文改中文
Ubuntu 24.04 LTS 修改软件源 Ubuntu 更改软件源 修改语言 无需输入命令,为Ubuntu 24.04系统添加中文智能拼音输入法 在 setting 的 system 中按下图操作 点击“Apply Changes”。需要管理员密码,安装完成后,退出登录,重新登…...
信创在医疗领域的应用:开启医疗信息化新时代
信创在医疗领域的应用:开启医疗信息化新时代 信创在医疗领域的应用:开启医疗信息化新时代信创医疗自助一体机杭医基于信创底座的健康医疗大数据平台厦门大学附属成功医院基于海光CPU的信创改造中科可控基于海光CPU的智慧医疗解决方案 信创在医疗领域的应…...
力扣-数组-303 区域和检索-数组不可变
解析 题目有点费解,大致应该是给出区间内的和,然后维护一个前缀和,为了防止越界,先填一个0进去,在构建的时候也要注意此时构建的dp的下标是i1,所以加的前缀和的下标是i。 代码 class NumArray { public:…...
【CSS】---- CSS 实现超过固定高度后出现展开折叠按钮
1. 实现效果 2. 实现方法 使用 JS 获取盒子的高度,来添加对应的按钮和样式;使用 CSS 的浮动效果,参考CSS 实现超过固定高度后出现展开折叠按钮;使用容器查询 – container 语法;使用 clamp 函数进行样式判断。 3. 优…...
二十项零信任相关的前沿和趋势性技术-MASQUE
影响力评级:较低 市场渗透率:不到目标受众的 1% 成熟度:孵化 定义:基于QUIC加密的多路复用应用程序底层 (MASQUE) 是一个 IETF 标准草案,可实现流量的安全传输和代理。 MASQUE全称为:Multiplexed Appli…...
【Docker】使用Dev Container进行开发
工作区 Dev Container 设置 新建一个文件夹 ./devcontainer 然后下面放 devcontainer.json 然后安装 vscode dev container 插件,然后 CtrlShiftP 启动 Container {"name": "PyTorch-Julia Development","image": "x66ccff/p…...
搭建一个基于Spring Boot的数码分享网站
搭建一个基于Spring Boot的数码分享网站可以涵盖多个功能模块,例如用户管理、数码产品分享、评论、点赞、收藏、搜索等。以下是一个简化的步骤指南,帮助你快速搭建一个基础的数码分享平台。 — 1. 项目初始化 使用 Spring Initializr 生成一个Spring …...
在线json格式化工具
在线json格式化工具,包括中文和英文版本,无需登录,无需费用,用完就走。 官网地址: https://json.openai2025.com 效果如下:...
leetcode300.最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 示例 1&…...
【个人学习记录】软件开发生命周期(SDLC)是什么?
软件开发生命周期(Software Development Life Cycle,SDLC)是一个用于规划、创建、测试和部署信息系统的结构化过程。它包含以下主要阶段: 需求分析(Requirements Analysis) 收集并分析用户需求定义系统目标…...
CTE与临时表:优劣势对比及使用场景分析
在数据库开发中,尤其是在复杂查询和优化中,**公共表表达式(CTE)和临时表(Temporary Table)**是两种常用的工具。尽管它们的功能有些相似,都是为了处理中间结果集,但它们的优劣势和使…...
Kali环境变量技巧(The Environment Variable Technique Used by Kali
Kali环境变量技巧 朋友们好,我们今天继续更新《黑客视角下的Kali Linux的基础与网络管理》中的管理用户环境变量。为了充分利用我们的黑客操作系统Kali Linux,我们需要理解和善于使用环境变量,这样会使我们的工具更具便利,甚至具…...
Ubuntu 24.04 LTS linux 文件权限
Ubuntu 24.04 LTS 文件权限 读权限 :允许查看文件的内容。写权限 (w):允许修改文件的内容。执行权限 (x):允许执行文件(对于目录来说,是进入目录的权限)。 文件权限通常与三类用户相关联: 文…...
多个版本JAVA切换(学习笔记)
多个版本JAVA切换 很多时候,我们电脑上会安装多个版本的java版本,java8,java11,java17等等,这时候如果想要切换java的版本,可以按照以下方式进行 1.检查当前版本的JAVA 同时按下 win r 可以调出运行工具…...
AI刷题-最小替换子串长度、Bytedance Tree 问题
目录 一、最小替换子串长度 问题描述 输入格式 输出格式 输入样例 1 输出样例 1 输入样例 2 输出样例 2 解题思路: 问题理解 数据结构选择 算法步骤 最终代码: 运行结果: 二、Bytedance Tree 问题 问题描述 输入格式 输…...
Android 项目依赖冲突问题:Duplicate class found in modules
问题描述与处理处理 1、问题描述 plugins {id com.android.application }android {compileSdk 34defaultConfig {applicationId "com.my.dialog"minSdk 21targetSdk 34versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.run…...
Webpack简述
一、为什么要构建工具 人类喜欢书写的代码以及开发方式计算机不喜欢,构建工具的作用就是让人类舒舒服服写自己喜欢的代码,然后一打包生成计算机喜欢的代码 第一个webpack自身仅仅是将我们引入的模块打包成一个文件(编译import)&am…...
ARM GCC编译器
ARM GCC编译器(GNU Compiler Collection for ARM)是GNU项目的一部分,专门用于编译针对ARM架构的代码。它是一个开源的工具链,支持多种编程语言,包括C、C和汇编语言。以下是关于ARM GCC编译器的详细解释及其作用&#x…...
CSS3 3D 转换介绍
CSS3 中的 3D 转换提供了一种在二维屏幕上呈现三维效果的方式,主要包括translate3d、rotate3d、scale3d等转换函数,下面来详细介绍: 1. 3D 转换的基本概念 坐标系 在 CSS3 的 3D 空间中,使用的是右手坐标系。X 轴是水平方向&…...
关于 Cursor 的一些学习记录
文章目录 1. 写在最前面2. Prompt Design2.1 Priompt v0.1:提示设计库的首次尝试2.2 注意事项 3. 了解 Cursor 的 AI 功能3.1 问题3.2 答案 4. cursor 免费功能体验5. 写在最后面6. 参考资料 1. 写在最前面 本文整理了一些学习 Cursor 过程中读到的或者发现的感兴趣…...
3. 后端验证前端Token
书接上回,后端将token返回给前端,前端存入cookie,每次前端给后端发送请求,后端是如何验证的。 若依是用过滤器来实现对请求的验证,过滤器的简单理解是每次发送请求的时候先发送给过滤器执行逻辑判断以及处理࿰…...
【LLM】Openai-o1及o1类复现方法
note 可以从更为本质的方案出发,通过分析强化学习的方法,看看如何实现o1,但其中的核心就是在于,如何有效地初始化策略、设计奖励函数、实现高效的搜索算法以及利用强化学习进行学习和优化。 文章目录 note一、Imitate, Explore, …...
与“神”对话:Swift 语言在 2025 中的云霓之望
0. 引子 夜深人静,是一片极度沉醉的黑,这便于我与深沉的 macbook 悄悄隐秘于其中。一股异香袭来,恍惚着,撸码中身心极度疲惫、头脑昏沉的我仿佛感觉到了一束淡淡的微光轻洒在窗边。 我的对面若隐若现逐渐浮现出一个熟悉的身影。他…...
设计模式-单例模式
定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 类图 类型 饿汉式 线程安全,调用效率高,但是不能延迟加载。 public class HungrySingleton {private static final HungrySingleton instancenew HungrySingleton();private …...
C#枚举类型携带额外数据的方法
Java里面的枚举类型可以定义很多属性,携带各种数据,然而C#里面的枚举类型只能代表数字,不能在枚举类型里面定义各种属性,导致某些应用场景使用起来不方便,但是可以利用C#里面的Attribute来解决这个问题。 例如…...
跨境电商使用云手机用来做什么呢?
随着跨境电商的发展,越来越多的卖家开始尝试使用云手机来协助他们的业务,这是因为云手机具有许多优势。那么,具体来说,跨境电商使用云手机可以做哪些事情呢? (一)实现多账号登录和管理 跨境电商…...
RabbitMQ-消息可靠性以及延迟消息
目录 消息丢失 一、发送者的可靠性 1.1 生产者重试机制 1.2 生产者确认机制 1.3 实现生产者确认 (1)开启生产者确认 (2)定义ReturnCallback (3)定义ConfirmCallback 二、MQ的持久化 2.1 数据持久…...
Mybatis plus中的BaseMapper与ServiceImpl
BaseMapper接口方法与ServiceImpl类方法的区别与联系 什么是BaseMapper?什么是ServiceImpl? BaseMapper 是 MyBatis-Plus 提供的一个基础 Mapper 接口,封装了常用的 CRUD 操作方法,如 selectById、insert、updateById、deleteBy…...
第三篇 Avaya IP Office的架构及其服务组成
所谓的架构,其实就是Solution,解决方案。一般就是如下几套: IPO primary IPO secondaryIPO primary IP500v2IPO primary IPO secondary IP500v2IPO primary IPO secondary IP500v2 Expansion Server(IP500v2,扩展)IPO primaryIPO 500v2 简单的解释…...
近红外简单ROI分析matlab(NIRS_SPM)
本次笔记主要想验证上篇近红外分析是否正确,因为叠加平均有不同的计算方法,一种是直接将每个通道的5分钟实时长单独进行叠加平均,另一种是将通道划分为1分钟的片段,将感兴趣的通道数据进行对应叠加平均,得到一个总平均…...
ESP32学习笔记_FreeRTOS(6)——Event and Notification
摘要(From AI): 这篇博客详细介绍了 FreeRTOS 中的事件组和任务通知机制,讲解了事件组如何通过位操作实现任务间的同步与通信,以及任务如何通过通知机制进行阻塞解除和数据传递。博客提供了多个代码示例,展示了如何使用事件组和任务通知在多任…...
多监控m3u8视频流,怎么获取每个监控的封面图(纯前端)
文章目录 1.背景2.问题分析3.解决方案3.1解决思路3.2解决过程3.2.1 封装播放组件3.2.2 隐形的视频div3.2.3 截取封面图 3.3 结束 1.背景 有这样一个需求: 给你一个监控列表,每页展示多个监控(至少12个,m3u8格式)&…...
ExpGCN:深度解析可解释推荐系统中的图卷积网络
一、引言 在当今信息爆炸的时代,推荐系统已成为电子商务和社交网络中不可或缺的工具,旨在为用户筛选出符合其兴趣的信息。传统的协同过滤(CF)技术通过挖掘用户与项目之间的交互记录来生成推荐,但这种方法简化了模型&a…...
ChatGPT Prompt 编写指南
一、第一原则:明确的意图 你需要明确地表达你的意图和要求,尽可能具体、描述性、详细地描述所需的上下文、你期望的结果等。你的要求越明确,越有希望获得你想要的答案。 糟糕的案例 ❌ 写一首关于 OpenAI 的诗。 更好的案…...
【脑机接口数据处理】 如何读取Trode 的.rec文件 原始数据?
文章目录 函数简介文件下载函数语法基本用法带时间跳过的用法带选项参数的用法输出结构使用示例 注意事项 MATLAB中读取Trodes文件的实用函数——readTrodesFileContinuous 在处理神经科学实验数据时,经常会遇到Trodes格式的文件。这些文件包含了丰富的神经信号数据…...
反转字符串中的单词 II:Swift 实现与详解
网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…...
蓝桥杯训练—矩形面积交
文章目录 一、题目二、示例三、解析四、代码 一、题目 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴,对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程写出两个矩形的交的面积 输入格式: 输入包含两行…...
如何设置HTTPS站点防御?
设置HTTPS站点防御涉及到多个层面的安全措施,包括但不限于配置Web服务器、应用安全头信息、使用内容安全策略(CSP)、启用HSTS和OCSP Stapling等。下面是一些关键的步骤来增强HTTPS网站的安全性: 1. 使用强加密协议和密钥交换算法…...
光谱相机如何还原色彩
多光谱通道采集 光谱相机设有多个不同波段的光谱通道,可精确记录每个波长的光强信息。如 8 到 16 个甚至更多的光谱通道,每个通道负责特定波长范围的光信息记录。这使得相机能分辨出不同光谱组合产生的相同颜色感知,而传统相机的传感器通常只…...
doris:导入概览
Apache Doris 提供了多种导入和集成数据的方法,您可以使用合适的导入方式从各种源将数据导入到数据库中。Apache Doris 提供的数据导入方式可以分为四类: 实时写入:应用程序通过 HTTP 或者 JDBC 实时写入数据到 Doris 表中,适用于…...