文件上传 分片上传
分片上传则是将一个大文件分割成多个小块分别上传,最后再由服务器合并成完整的文件。这种做法的好处是可以并行处理多个小文件,提高上传效率;同时,如果某一部分上传失败,只需要重传这一部分,不影响其他部分。
初步实现
后端代码
/*** 分片上传** @param file 上传的文件* @param start 文件开始上传的位置* @param fileName 文件名称* @return 上传结果*/
@PostMapping("/fragmentUpload")
@ResponseBody
public AjaxResult fragmentUpload(@RequestParam("file") MultipartFile file, @RequestParam("start") long start, @RequestParam("fileName") String fileName) {try {// 检查上传目录是否存在,如果不存在则创建File directory = new File(uploadPath);if (!directory.exists()) {directory.mkdirs();}// 设置上传文件的目标路径File targetFile = new File(uploadPath +File.separator+ fileName);// 创建 RandomAccessFile 对象以便进行文件的随机读写操作RandomAccessFile randomAccessFile = new RandomAccessFile(targetFile, "rw");// 获取 RandomAccessFile 对应的 FileChannelFileChannel channel = randomAccessFile.getChannel();// 设置文件通道的位置,即从哪里开始写入文件内容channel.position(start);// 从 MultipartFile 对象的资源通道中读取文件内容,并写入到指定位置channel.transferFrom(file.getResource().readableChannel(), start, file.getSize());// 关闭文件通道和 RandomAccessFile 对象channel.close();randomAccessFile.close();// 返回上传成功的响应return AjaxResult.success("上传成功");} catch (Exception e) {// 捕获异常并返回上传失败的响应return AjaxResult.error("上传失败");}
}/*** 检测文件是否存在* 如果文件存在,则返回已经存在的文件大小。* 如果文件不存在,则返回 0,表示前端从头开始上传该文件。* @param filename* @return*/
@GetMapping("/checkFile")
@ResponseBody
public AjaxResult checkFile(@RequestParam("filename") String filename) {File file = new File(uploadPath+File.separator + filename);if (file.exists()) {return AjaxResult.success(file.length());} else {return AjaxResult.success(0L);}
}
前端
var prefix = ctx + "/kuroshiro/file-upload";// 每次上传大小
const chunkSize = 1 * 1024 * 1024;/*** 开始上传*/
function startUpload(type) {const fileInput = document.getElementById('fileInput');const file = fileInput.files[0];if (!file) {alert("请选择文件");return;}if(type == 1){checkFile(filename).then(start => {uploadFile(file, start,Math.min(start + chunkSize, file.size));})}
}/*** 检查是否上传过* @param filename* @returns {Promise<unknown>}*/
function checkFile(filename) {return $fetch(prefix+`/checkFile?filename=${filename}`);
}/*** 开始分片上传* @param file 文件* @param start 开始位置* @param end 结束位置*/
function uploadFile(file, start,end) {if(start < end){const chunk = file.slice(start, end);const formData = new FormData();formData.append('file', chunk);formData.append('start', start);formData.append('fileName', file.name);$fetch(prefix+'/fragmentUpload', {method: 'POST',body: formData}).then(response => {console.log(`分片 ${start} - ${end} 上传成功`);// 递归调用uploadFile(file,end,Math.min(end + chunkSize, file.size))})}}function $fetch(url,requestInit){return new Promise((resolve, reject) => {fetch(url,requestInit).then(response => {if (!response.ok) {throw new Error('请求失败');}return response.json();}).then(data => {if (data.code === 0) {resolve(data.data);} else {console.error(data.msg);reject(data.msg)}}).catch(error => {console.error(error);reject(error)});});}
以上虽然实现的分片上传,但是它是某种意义上来说还是与整体上传差不多,它是一段一段的上传,某段上传失败后,后续的就不会再继续上传;不过比起整体上传来说,它会保存之前上传的内容,下一个上传时,从之前上传的位置接着上传。不用整体上传。下面进行优化。
优化
首先,之前的分片上传,后端是直接写入了一个文件中了,所以只能顺序的上传写入,虽然可以保存上传出错之前的内容,但是整体上看来是速度也不行。
优化逻辑:把分片按顺序单独保存下来,等到所有分片都上传成功后,把所有分片合并成文件。这样上传的时候就不用等着上一个上传成功才上传下一个了。
后端代码
/**
* 分片上传* @param file 文件* @param chunkIndex 分片下标*/
@PostMapping("/uploadChunk")
@ResponseBody
public AjaxResult uploadChunk(@RequestParam("file") MultipartFile file, @RequestParam("chunkIndex") int chunkIndex,@RequestParam("fileName") String fileName) {String uploadDirectory = chunkUploadPath+File.separator+fileName;File directory = new File(uploadDirectory);if (!directory.exists()||directory.isFile()) {directory.mkdirs();}String filePath = uploadDirectory + File.separator + fileName+ "_" + chunkIndex;try (OutputStream os = new FileOutputStream(filePath)) {os.write(file.getBytes());return AjaxResult.success("分片"+(chunkIndex+1)+"上传成功");}catch (Exception e){// 保存失败后如果文件建立了就删除,下次上传时重新保存,避免文件内容错误File chunkFile = new File(filePath);if(chunkFile.exists()) chunkFile.delete();e.printStackTrace();return AjaxResult.error("分片"+(chunkIndex+1)+"上传失败");}}/*** 检测分片是否存在* 如果文件存在,则返回已经存在的分片下标集合。存在的就不上传* 如果文件不存在,则返回空集合,表示前端从头开始上传该文件* @param fileName* @return*/
@GetMapping("/checkChunk")
@ResponseBody
public AjaxResult checkChunk(@RequestParam("fileName") String fileName) {String uploadDirectory = chunkUploadPath+File.separator+fileName;List<Integer> list = new ArrayList<>();File file = new File(uploadDirectory);// 文件目录不存在if(!file.exists()||file.isFile()) return AjaxResult.success(list);File[] files = file.listFiles();// 文件目录下没有分片文件if(files == null) return AjaxResult.success(list);// 返回存在分片下标集合return AjaxResult.success(Arrays.stream(files).map(item->Integer.valueOf(item.getName().substring(item.getName().lastIndexOf("_")+1))).collect(Collectors.toList()));
}// 合并文件分片@PostMapping("/mergeChunks")@ResponseBodypublic AjaxResult mergeChunks(@RequestParam("fileName") String fileName, @RequestParam("totalChunks") int totalChunks) {String uploadDirectory = chunkUploadPath+File.separator+fileName;String mergedFilePath = uploadPath +File.separator+ fileName;try (OutputStream os = new FileOutputStream(mergedFilePath, true)) {for (int i = 0; i < totalChunks; i++) {Path chunkFilePath = Paths.get(uploadDirectory +File.separator+ fileName + "_" + i);Files.copy(chunkFilePath, os);Files.delete(chunkFilePath);}return AjaxResult.success();}catch (Exception e){e.printStackTrace();return AjaxResult.error(e.getMessage());}}
前端代码
/*** 开始上传*/
function startUpload(type) {const fileInput = document.getElementById('fileInput');const file = fileInput.files[0];if (!file) {alert("请选择文件");return;}const filename = file.name;if(type == 1){checkFile(filename).then(start => {uploadFile(file, start,Math.min(start + chunkSize, file.size));})}if(type == 2){checkChunk(filename).then(arr => {uploadChunk(file, arr);})}
}/**
* 切割文件为多个分片
* @param file
* @returns {*[]}
*/
function sliceFile(file) {const chunks = [];let offset = 0;while (offset < file.size) {const chunk = file.slice(offset, offset + chunkSize);chunks.push(chunk);offset += chunkSize;}return chunks;
}
/**
* 检查是否上传过
* @param filename
* @returns {Promise<unknown>}
*/
function checkChunk(filename) {return $fetch(prefix+`/checkChunk?fileName=${filename}`);
}/**
* 开始分片上传
* @param file 文件
* @param exists 存在的分片下标
*/
function uploadChunk(file,exists) {const chunkArr = sliceFile(file);Promise.all(chunkArr.map((chunk, index) => {if(!exists.includes(index)){const formData = new FormData();formData.append('file', chunk);formData.append('fileName', file.name);formData.append('chunkIndex', index);return $fetch(prefix+'/uploadChunk', {method: 'POST',body: formData});}})).then(uploadRes=> {// 合并分片const formData = new FormData();formData.append('fileName', file.name);formData.append('totalChunks', chunkArr.length);$fetch(prefix + '/mergeChunks', {method: 'POST',body:formData,}).then(mergeRes=>{console.log("合并成功")});});
}
以上优化后所有分片可以同时上传,所有分片上传都成功后进行合并。
最后是完整代码
@Controller()
@RequestMapping("/kuroshiro/file-upload")
public class FileUploadController {private String prefix = "kuroshiro/fragmentUpload";// 文件保存目录private final String uploadPath = RuoYiConfig.getUploadPath();// 分片保存目录private final String chunkUploadPath = uploadPath+File.separator+"chunks";/*** demo* @return*/@GetMapping("/demo")public String demo() {return prefix+"/demo";}/*** 分片上传** @param file 上传的文件* @param start 文件开始上传的位置* @param fileName 文件名称* @return 上传结果*/@PostMapping("/fragmentUpload")@ResponseBodypublic AjaxResult fragmentUpload(@RequestParam("file") MultipartFile file, @RequestParam("start") long start, @RequestParam("fileName") String fileName) {try {// 检查上传目录是否存在,如果不存在则创建File directory = new File(uploadPath);if (!directory.exists()) {directory.mkdirs();}// 设置上传文件的目标路径File targetFile = new File(uploadPath +File.separator+ fileName);// 创建 RandomAccessFile 对象以便进行文件的随机读写操作RandomAccessFile randomAccessFile = new RandomAccessFile(targetFile, "rw");// 获取 RandomAccessFile 对应的 FileChannelFileChannel channel = randomAccessFile.getChannel();// 设置文件通道的位置,即从哪里开始写入文件内容channel.position(start);// 从 MultipartFile 对象的资源通道中读取文件内容,并写入到指定位置channel.transferFrom(file.getResource().readableChannel(), start, file.getSize());// 关闭文件通道和 RandomAccessFile 对象channel.close();randomAccessFile.close();// 返回上传成功的响应return AjaxResult.success("上传成功");} catch (Exception e) {// 捕获异常并返回上传失败的响应return AjaxResult.error("上传失败");}}/*** 检测文件是否存在* 如果文件存在,则返回已经存在的文件大小。* 如果文件不存在,则返回 0,表示前端从头开始上传该文件。* @param filename* @return*/@GetMapping("/checkFile")@ResponseBodypublic AjaxResult checkFile(@RequestParam("filename") String filename) {File file = new File(uploadPath+File.separator + filename);if (file.exists()) {return AjaxResult.success(file.length());} else {return AjaxResult.success(0L);}}/*** 分片上传* @param file 文件* @param chunkIndex 分片下标*/@PostMapping("/uploadChunk")@ResponseBodypublic AjaxResult uploadChunk(@RequestParam("file") MultipartFile file, @RequestParam("chunkIndex") int chunkIndex,@RequestParam("fileName") String fileName) {String uploadDirectory = chunkUploadPath+File.separator+fileName;File directory = new File(uploadDirectory);if (!directory.exists()||directory.isFile()) {directory.mkdirs();}String filePath = uploadDirectory + File.separator + fileName+ "_" + chunkIndex;try (OutputStream os = new FileOutputStream(filePath)) {os.write(file.getBytes());return AjaxResult.success("分片"+(chunkIndex+1)+"上传成功");}catch (Exception e){// 保存失败后如果文件建立了就删除,下次上传时重新保存,避免文件内容错误File chunkFile = new File(filePath);if(chunkFile.exists()) chunkFile.delete();e.printStackTrace();return AjaxResult.error("分片"+(chunkIndex+1)+"上传失败");}}/*** 检测分片是否存在* 如果文件存在,则返回已经存在的分片下标集合。存在的就不上传* 如果文件不存在,则返回空集合,表示前端从头开始上传该文件* @param fileName* @return*/@GetMapping("/checkChunk")@ResponseBodypublic AjaxResult checkChunk(@RequestParam("fileName") String fileName) {String uploadDirectory = chunkUploadPath+File.separator+fileName;List<Integer> list = new ArrayList<>();File file = new File(uploadDirectory);// 文件目录不存在if(!file.exists()||file.isFile()) return AjaxResult.success(list);File[] files = file.listFiles();// 文件目录下没有分片文件if(files == null) return AjaxResult.success(list);// 返回存在分片下标集合return AjaxResult.success(Arrays.stream(files).map(item->Integer.valueOf(item.getName().substring(item.getName().lastIndexOf("_")+1))).collect(Collectors.toList()));}// 合并文件分片@PostMapping("/mergeChunks")@ResponseBodypublic AjaxResult mergeChunks(@RequestParam("fileName") String fileName, @RequestParam("totalChunks") int totalChunks) {String uploadDirectory = chunkUploadPath+File.separator+fileName;String mergedFilePath = uploadPath +File.separator+ fileName;try (OutputStream os = new FileOutputStream(mergedFilePath, true)) {for (int i = 0; i < totalChunks; i++) {Path chunkFilePath = Paths.get(uploadDirectory +File.separator+ fileName + "_" + i);Files.copy(chunkFilePath, os);Files.delete(chunkFilePath);}File chunkDir = new File(uploadDirectory);if (chunkDir.exists()) chunkDir.delete();return AjaxResult.success();}catch (Exception e){e.printStackTrace();return AjaxResult.error(e.getMessage());}}}
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><th:block th:include="include :: header('分片上传')" />
</head>
<body class="gray-bg">
<div class="container-div" id="chunk-div"><div class="row"><div class="col-sm-12 search-collapse"><form id="formId"><div class="select-list"><ul><li><label>选择文件:</label><input type="file" id="fileInput"/></li><li><a class="btn btn-primary btn-rounded btn-sm" @click="startUpload(1)"><i class="fa fa-upload"></i> 开始上传1</a><a class="btn btn-primary btn-rounded btn-sm" @click="startUpload(2)"><i class="fa fa-upload"></i> 开始上传2</a></li></ul></div></form></div><div class="col-sm-12" style="padding-left: 0;"><div class="ibox"><div class="ibox-content"><h3>上传进度</h3><ul class="sortable-list connectList agile-list" v-if="uploadMsg"><li v-for="item in uploadMsg" :class="item.status+'-element'">{{item.title}}<div class="agile-detail">{{item.result}}</div></li></ul></div></div></div></div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">var prefix = ctx + "/kuroshiro/file-upload";new Vue({el: '#chunk-div',data: {// 每次上传大小chunkSize: 100 * 1024 * 1024,uploadMsg:{},startTime:0,},methods: {/*** 开始上传*/startUpload: function(type){const fileInput = document.getElementById('fileInput');const file = fileInput.files[0];if (!file) {alert("请选择文件");return;}const filename = file.name;this.uploadMsg = {};this.startTime = (new Date()).getTime();Vue.set(this.uploadMsg, 'checkMsg', {title:`文件检测`,result: "检测中... ...",status:"info"});if(type == 1){this.checkFile(filename).then(start => {this.uploadMsg['checkMsg'].result = `检测成功:已存在文件,大小为 ${start}`this.uploadFile(file, start,Math.min(start + this.chunkSize, file.size));},err => {this.uploadMsg['checkMsg'].result = `检测失败:${err}`})}if(type == 2){this.checkChunk(filename).then(arr => {this.uploadMsg['checkMsg'].result = `检测成功:已存在文件分片 ${arr.length}`this.uploadChunk(file, arr);},err => {this.uploadMsg['checkMsg'].result = `检测失败:${err}`this.uploadMsg['checkMsg'].status = `info`})}},/*** 检查是否上传过* @param filename* @returns {Promise<unknown>}*/checkFile: function(filename) {return this.$fetch(prefix+`/checkFile?filename=${filename}`);},/*** 开始分片上传* @param file 文件* @param start 开始位置* @param end 结束位置*/uploadFile: function(file, start,end) {if(start < end){const chunk = file.slice(start, end);const formData = new FormData();formData.append('file', chunk);formData.append('start', start);formData.append('fileName', file.name);Vue.set(this.uploadMsg, 'uploadMsg_'+start, {title:`分片 ${start} - ${end} 上传`,result: "上传中... ...",status:"info"});this.$fetch(prefix+'/fragmentUpload', {method: 'POST',body: formData}).then(response => {this.uploadMsg['uploadMsg_'+start].result = `上传成功`;// 递归调用this.uploadFile(file,end,Math.min(end + this.chunkSize, file.size))},err=>{this.uploadMsg['uploadMsg_'+start].result = `上传失败:${err}`;this.uploadMsg['uploadMsg_'+start].status = `danger`;})}else{this.uploadMsg['uploadSuccess'] = {title:`文件已上传`,result:`耗时:`+((new Date()).getTime()-this.startTime),status:"info"};}},/*** 切割文件为多个分片* @param file* @returns {*[]}*/sliceFile: function(file) {const chunks = [];let offset = 0;while (offset < file.size) {const chunk = file.slice(offset, offset + this.chunkSize);chunks.push(chunk);offset += this.chunkSize;}return chunks;},/*** 检查是否上传过* @param filename* @returns {Promise<unknown>}*/checkChunk: function(filename) {return this.$fetch(prefix+`/checkChunk?fileName=${filename}`);},/*** 开始分片上传* @param file 文件* @param exists 存在的分片下标*/uploadChunk: function(file,exists) {const chunkArr = this.sliceFile(file);Promise.all(chunkArr.map(async (chunk, index) => {if (!exists.includes(index)) {const formData = new FormData();formData.append('file', chunk);formData.append('fileName', file.name);formData.append('chunkIndex', index);Vue.set(this.uploadMsg, "upload_" + index, {title: `分片 ${index + 1} 上传`,result: "上传中... ...",status: "info"});return new Promise((resolve, reject) => {this.$fetch(prefix+'/uploadChunk', {method: 'POST',body: formData}).then(res => {resolve(res)this.uploadMsg["upload_"+index].result = "上传成功";},err => {reject(err)this.uploadMsg["upload_"+index].result = err;this.uploadMsg["upload_"+index].status = "danger";});})}})).then(uploadRes=> {this.uploadMsg["uploadSuccess"] = {title:`上传成功`,result: "耗时:"+((new Date()).getTime()-this.startTime),status:"info"};// 合并分片const formData = new FormData();formData.append('fileName', file.name);formData.append('totalChunks', chunkArr.length);Vue.set(this.uploadMsg, 'mergeChunks', {title:`合并分片`,result: "合并中... ...",status:"info"});this.$fetch(prefix + '/mergeChunks', {method: 'POST',body:formData,}).then(mergeRes=>{this.uploadMsg["mergeChunks"].result = "合并成功";},err => {this.uploadMsg["mergeChunks"].result = `合并失败:${err}`;this.uploadMsg["mergeChunks"].status = "danger";});});},$fetch: function(url,requestInit){return new Promise((resolve, reject) => {fetch(url,requestInit).then(response => {if (!response.ok) {throw new Error('请求失败');}return response.json();}).then(data => {if (data.code === 0) {resolve(data.data);} else {reject(data.msg)}}).catch(error => {reject(error)});});},}});</script>
</body>
</html>相关文章:
文件上传 分片上传
分片上传则是将一个大文件分割成多个小块分别上传,最后再由服务器合并成完整的文件。这种做法的好处是可以并行处理多个小文件,提高上传效率;同时,如果某一部分上传失败,只需要重传这一部分,不影响其他部分…...
【0391】Postgres内核 checkpointer process ① 启动初始化
相关文章: 【0108】checkpointer运行原理(概念篇)(1) 【0278】checkpointer 共享内存(CheckpointerShmem)初始化(3) 文章目录 1. 启动 checkpointer process1.1 初始化 checkpointer PID1.2 注册 signal1.3 初始化 last checkpoint time2. 确认 config 的 shared memo…...
链路追踪SkyWalking
链路追踪 链路追踪作用链路追踪的关键概念链路追踪的工作原理常用链路追踪工具链路追踪的实现步骤链路追踪的典型场景 SkyWalkingSkyWalking 的主要功能SkyWalking 的架构安装 SkyWalking从 SkyWalking 的官方 GitHub 仓库 下载最新版本。配置后端存储SkyWalking使用࿰…...
Uniapp判断设备是安卓还是 iOS,并调用不同的方法
在 UniApp 中,可以通过 uni.getSystemInfoSync() 方法来获取设备信息,然后根据系统类型判断当前设备是安卓还是 iOS,并调用不同的方法。 示例代码 export default {onLoad() {this.checkPlatform();},methods: {checkPlatform() {// 获取系…...
计算机网络 (42)远程终端协议TELNET
前言 Telnet(Telecommunication Network Protocol)是一种网络协议,属于TCP/IP协议族,主要用于提供远程登录服务。 一、概述 Telnet协议是一种远程终端协议,它允许用户通过终端仿真器连接到远程主机,并在远程…...
rtthread学习笔记系列-- 23 环形缓冲块 ringblock
文章目录 23 环形缓冲块 ringblock23.1 初始化23.2 PUT & GET 块23.3 块释放23.4 rt_rbb_blk_queue_get23.5 rt_rbb_blk_alloc https://github.com/wdfk-prog/RT-Thread-Study 23 环形缓冲块 ringblock 环形块状缓冲区简称为:rbb。与传统的环形缓冲区不同的是&…...
HunyuanVideo 文生视频模型实践
HunyuanVideo 文生视频模型实践 flyfish 运行 HunyuanVideo 模型使用文本生成视频的推荐配置(batch size 1): 模型分辨率(height/width/frame)峰值显存HunyuanVideo720px1280px129f60GHunyuanVideo544px960px129f45G 本项目适用于使用 N…...
Qt——QTableWidget 限制单元格输入范围的方法(正则表达式输入校验法、自定义代理类MyItemDelegrate)
【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》...
深度学习论文: CAS-ViT: Convolutional Additive Self-attention Vision Transformers
深度学习论文: CAS-ViT: Convolutional Additive Self-attention Vision Transformers for Efficient Mobile Applications CAS-ViT: Convolutional Additive Self-attention Vision Transformers for Efficient Mobile Applications PDF:https://arxiv.org/pdf/2408.03703 PyT…...
PyCharm文档管理
背景:使用PyCharmgit做文档管理 需求:需要PyCharm自动识别docx/xslx/vsdx等文件类型,并在PyCharm内点击文档时唤起系统内关联应用(如word、excel、visio) 设置步骤: 1、file -》 settings -》file types 2、在Files opened i…...
QNAP 上常用的几款软件
当我们谈到 NAS(Network Attached Storage)时,QNAP 凭借多年的产品迭代、稳定的硬件性能和不断丰富的软件生态,已成为很多家庭及中小型企业的首选。除了存储本身,QNAP 提供的各种官方软件和应用,也为用户带…...
LabVIEW智能水肥一体灌溉控制系统
本文详细介绍了一种基于LabVIEW的智能水肥一体灌溉控制系统的设计与实现。该系统采用模糊控制策略,能够自动调节土壤湿度和肥液浓度,满足不同作物在不同生长阶段的需求,有效提高水肥利用效率,对现代精准农业具有重要的实践和推广价…...
提问:玩游戏输入法总弹出来咋回事哎
玩游戏时输入法总弹出来的问题,通常与电脑的输入法设置、操作系统配置以及游戏程序的兼容性有关。以下是一些常见的解决方法: 一、修改输入法快捷键 禁用不必要的输入法: 在系统的语言设置中,暂时禁用非活动的输入法,…...
链家房价数据爬虫和机器学习数据可视化预测
完整源码项目包获取→点击文章末尾名片!...
【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解
分布式系统理论:CAP 与 BASE 详解 一、CAP 定理 背景与定义:1998 年由加州大学科学家埃里克布鲁尔提出,分布式系统存在一致性(Consistency)、可用性(Availability)、分区容错性(Part…...
第十二章:算法与程序设计
文章目录: 一:基本概念 1.算法与程序 1.1 算法 1.2 程序 2.编译预处理 3.面向对象技术 4.程序设计方法 5.SOP标志作业流程 6.工具 6.1 自然语言 6.2 流程图 6.3 N/S图 6.4 伪代码 6.5 计算机语言 二:程序设计 基础 1.常数 …...
RAG技术:是将知识库的文档和问题共同输入到LLM中
RAG技术 RAG技术是将知识库的文档和问题共同输入到LLM中 RAG技术是先从知识库中检索出与问题相关的文档片段,然后将这些检索到的文档片段与问题一起输入到LLM中进行回答。具体过程如下: 文本分块 由于LLM的上下文窗口有限,需要将长文本资料分割成较小的块,以便LLM能够有…...
持续集成 01|Gitee介绍、Pycharm使用Gitee
目录 一、理论 二、 git的简介与安装 三、Gitee 1、注册网易163邮箱 2、注册Gitee账号 3、git和gitee管理代码工作原理 三、PyCharm安装配置Gitee 四、Pycharm使用Gitee插件的五种场景 1、将 Gitee的新仓库 Checkout(检出)到 Pycharm中 2、推送…...
信息安全、网络安全和数据安全的区别和联系
信息安全、网络安全和数据安全是信息安全领域的三大支柱,它们之间既存在区别又相互联系。以下是对这三者的详细比较: 一.区别 1.信息安全 定义 信息安全是指为数据处理系统建立和采用的技术和管理的安全保护,保护计算机硬件、软件和数据不…...
C++实现设计模式---抽象工厂模式 (Abstract Factory)
抽象工厂模式 (Abstract Factory) 抽象工厂模式 是一种创建型设计模式,提供一个接口,用于创建一组相关或互相依赖的对象,而无需指定它们的具体类。 意图 提供一个创建一组相关对象的接口,而无需指定它们的具体类。解决产品对象之…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
