java实现文件分片上传并且断点续传
文章目录
- 什么是断点续传
- 后端实现
- JAVA实现大文件分片上传断点续传
什么是断点续传
用户上传大文件,网络差点的需要历时数小时,万一线路中断,不具备断点续传的服务器就只能从头重传,而断点续传就是,允许用户从上传断线的地方继续传送,这样大大减少了用户的烦恼。
解决上传大文件服务器内存不够的问题
解决如果因为其他因素导致上传终止的问题,并且刷新浏览器后仍然能够续传,重启浏览器(关闭浏览器后再打开)仍然能够继续上传,重启电脑后仍然能够上传
检测上传过程中因网络波动导致文件出现了内容丢失那么需要自动检测并且从新上传
解决方案
前端
需要进行分割上传的文件
需要对上传的分片文件进行指定文件序号
需要监控上传进度,控制进度条
上传完毕后需要发送合并请求
Blob 对象,操作文件
后端
上传分片的接口
合并分片的接口
获取分片的接口
其他工具方法,用于辅助
前端端需要注意的就是: 文件的切割,和进度条
后端需要注意的就是: 分片存储的地方和如何进行合并分片
后端实现
package com.controller.commontools.fIleupload;import com.alibaba.fastjson.JSON;
import com.application.Result;
import com.container.ArrayByteUtil;
import com.encryption.hash.HashUtil;
import com.file.FileUtils;
import com.file.FileWebUpload;
import com.file.ReadWriteFileUtils;
import com.function.impl.ExecutorUtils;
import com.path.ResourceFileUtil;
import com.string.PatternCommon;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;@RestController
@RequestMapping("/fileslice")
public class FIleSliceUploadController {private final String identification="-slice-";private final String uploadslicedir="uploads"+File.separator+"slice"+File.separator;//分片目录private final String uploaddir="uploads"+File.separator+"real"+File.separator;//实际文件目录//获取分片@GetMapping("/testing/{fileName}/{fileSlicSize}/{fileSize}")public Result testing(@PathVariable String fileName,@PathVariable long fileSlicSize,@PathVariable long fileSize ) throws Exception {String dir = fileNameMd5Dir(fileName,fileSize);String absoluteFilePathAndCreate = ResourceFileUtil.getAbsoluteFilePathAndCreate(uploadslicedir)+File.separator+dir;File file = new File(absoluteFilePathAndCreate);if (file.exists()) {List<String> filesAll = FileUtils.getFilesAll(file.getAbsolutePath());if (filesAll.size()<2){//分片缺少 删除全部分片文件 ,从新上传FileUtils.delFilesAllReview(absoluteFilePathAndCreate,true);return Result.Error();}//从小到大文件进行按照序号排序,和判断分片是否损坏List<String> collect = fileSliceIsbadAndSort(file, fileSlicSize);//获取最后一个分片String fileSliceName = collect.get(collect.size() - 1);fileSliceName = new File(fileSliceName).getName();int code = fileId(fileSliceName);//服务器的分片总大小必须小于或者等于文件的总大小if ((code*fileSlicSize)<=fileSize) {Result result = new Result();String finalFileSliceName = fileSliceName;String str = PatternCommon.renderString("{\"code\":\"$[code]\",\"fileSliceName\":\"${fileSliceName}\"}", new HashMap<String, String>() {{put("code", String.valueOf(code));put("fileSliceName", finalFileSliceName);}});result.setData(JSON.parse(str));return result;}else {//分片异常 ,删除全部分片文件,从新上传FileUtils.delFilesAllReview(absoluteFilePathAndCreate,true);return Result.Error();}}//不存在return Result.Error();}@PostMapping(value = "/uploads")public Result uploads(HttpServletRequest request) {String fileSliceName = request.getParameter("fileSliceName");long fileSize = Long.parseLong(request.getParameter("fileSize")); //文件大小String dir = fileSliceMd5Dir(fileSliceName,fileSize);String absoluteFilePathAndCreate = ResourceFileUtil.getAbsoluteFilePathAndCreate(uploadslicedir+dir);FileWebUpload.fileUpload(absoluteFilePathAndCreate,fileSliceName,request);int i = fileId(fileSliceName); //返回上传成功的文件id,用于前端计算进度Result result=new Result();result.setData(i);return result;}// 合并分片@GetMapping(value = "/merge-file-slice/{fileSlicNamee}/{fileSlicSize}/{fileSize}")public Result mergeFileSlice(@PathVariable String fileSlicNamee,@PathVariable long fileSlicSize,@PathVariable long fileSize ) throws Exception {int l =(int) Math.ceil((double) fileSize / fileSlicSize); //有多少个分片String dir = fileSliceMd5Dir(fileSlicNamee,fileSize); //分片所在的目录String absoluteFilePathAndCreate = ResourceFileUtil.getAbsoluteFilePathAndCreate(uploadslicedir+dir);File file=new File(absoluteFilePathAndCreate);if (file.exists()){List<String> filesAll = FileUtils.getFilesAll(file.getAbsolutePath());//阻塞循环判断是否还在上传 ,解决前端进行ajax异步上传的问题int beforeSize=filesAll.size();while (true){Thread.sleep(1000);//之前分片数量和现在分片数据只差,如果大于1那么就在上传,那么继续filesAll = FileUtils.getFilesAll(file.getAbsolutePath());if (filesAll.size()-beforeSize>=1){beforeSize=filesAll.size();//继续检测continue;}//如果是之前分片和现在的分片相等的,那么在阻塞2秒后检测是否发生变化,如果还没变化那么上传全部完成,可以进行合并了//当然这不是绝对的,只能解决网络短暂的波动,因为有可能发生断网很长时间,网络恢复后文件恢复上传, 这个问题是避免不了的,所以我们在下面的代码进行数量的效验// 因为我们不可能一直等着他网好,所以如果1~3秒内没有上传新的内容,那么我们默认判定上传完毕if (beforeSize==filesAll.size()){Thread.sleep(2000);filesAll = FileUtils.getFilesAll(file.getAbsolutePath());if (beforeSize==filesAll.size()){break;}}}//分片数量效验if (filesAll.size()!=l){//分片缺少 ,删除全部分片文件,从新上传FileUtils.delFilesAllReview(absoluteFilePathAndCreate,true);return Result.Error();}//获取实际的文件名称,组装路径String realFileName = realFileName(fileSlicNamee);String realFileNamePath = ResourceFileUtil.getAbsoluteFilePathAndCreate(uploaddir+ realFileName);//从小到大文件进行按照序号排序 ,和检查分片文件是否有问题List<String> collect = fileSliceIsbadAndSort(file, fileSlicSize);int fileSliceSize = collect.size();List<Future<?>> futures = new ArrayList<>();// 将文件按照序号进行合并 ,算出Runtime.getRuntime().availableProcessors()个线程 ,每个线程需要读取多少分片, 和每个线程需要读取多少字节大小//有人会说一个分片一个线程不行吗,你想想如果上千或者上万分片的话,你创建这么多的线程需要多少时间,以及线程切换上下文切换和销毁需要多少时间? // 就算使用线程池,也顶不住啊,你内存又有多大,能存下多少队列?,并发高的话直接怼爆int availableProcessors = Runtime.getRuntime().availableProcessors();//每个线程读取多少文件int readFileSize = (int)Math.ceil((double)fileSliceSize / availableProcessors);//每个线程需要读取的文件大小long readSliceSize = readFileSize * fileSlicSize;for (int i = 0; i < availableProcessors; i++) {int finalI = i;Future<?> future = ExecutorUtils.createFuture("FIleSliceUploadController",()->{//每个线程需要读取多少字节byte[] bytes=new byte[(int) readSliceSize];int index=0;for (int i1 = finalI *readFileSize,i2 = readFileSize*(finalI+1)>fileSliceSize?fileSliceSize:readFileSize*(finalI+1); i1 < i2; i1++) {try ( RandomAccessFile r = new RandomAccessFile(collect.get(i1), "r");){r.read(bytes, (int)(index*fileSlicSize),(int)fileSlicSize);} catch (IOException e) {e.printStackTrace();}index++;}if(finalI==availableProcessors-1){//需要调整数组bytes = ArrayByteUtil.getActualBytes(bytes);}try ( RandomAccessFile w = new RandomAccessFile(realFileNamePath, "rw");){//当前文件写入的位置w.seek(finalI*readSliceSize);w.write(bytes);} catch (IOException e) {e.printStackTrace();}});futures.add(future);}//阻塞到全部线程执行完毕后ExecutorUtils.waitComplete(futures);//删除全部分片文件FileUtils.delFilesAllReview(absoluteFilePathAndCreate,true);}else {//没有这个分片相关的的目录return Result.Error();}return Result.Ok();}//获取分片文件的目录private String fileSliceMd5Dir(String fileSliceName,long fileSize){int i = fileSliceName.indexOf(identification) ;String substring = fileSliceName.substring(0, i);String dir = HashUtil.md5(substring+fileSize);return dir;}//通过文件名称获取文件目录private String fileNameMd5Dir(String fileName,long fileSize){return HashUtil.md5(fileName+fileSize);}//获取分片的实际文件名private String realFileName(String fileSliceName){int i = fileSliceName.indexOf(identification) ;String substring = fileSliceName.substring(0, i);return substring;}//获取文件序号private int fileId(String fileSliceName){int i = fileSliceName.indexOf(identification)+identification.length() ;String fileId = fileSliceName.substring(i);return Integer.parseInt(fileId);}//判断是否损坏private List<String> fileSliceIsbadAndSort(File file,long fileSlicSize) throws Exception {String absolutePath = file.getAbsolutePath();List<String> filesAll = FileUtils.getFilesAll(absolutePath);if (filesAll.size()<1){//分片缺少,删除全部分片文件 ,从新上传FileUtils.delFilesAllReview(absolutePath,true);throw new Exception("分片损坏");}//从小到大文件进行按照序号排序List<String> collect = filesAll.stream().sorted((a, b) -> fileId(a) - fileId(b)).collect(Collectors.toList());//判断文件是否损坏,将文件排序后,进行前后序号相差大于1那么就代表少分片了for (int i = 0; i < collect.size()-1; i++) {//检测分片的连续度if (fileId(collect.get(i)) - fileId(collect.get(i+1))!=-1) {//分片损坏 删除全部分片文件 ,从新上传FileUtils.delFilesAllReview(absolutePath,true);throw new Exception("分片损坏");}//检测分片的完整度if (new File(collect.get(i)).length()!=fileSlicSize) {//分片损坏 删除全部分片文件 ,从新上传FileUtils.delFilesAllReview(absolutePath,true);throw new Exception("分片损坏");}}return collect;}
}
JAVA实现大文件分片上传断点续传
import org.springframework.web.multipart.MultipartFile;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang3.StringUtils;
import lombok.extern.slf4j.Slf4j;
import java.text.DecimalFormat;
import java.io.*;@Slf4j
public class FileSliceUpload {// 文件上传地址 private final String uploadPath = "/data/upload/";/** * @description: 文件分片上传 * @date: 2024/3/12 15:25 * @param fileSliceDTO * @return boolean */public boolean fileUpload(FileSliceDTO fileSliceDTO) {// 当前分片序号Integer chunkNumber = fileSliceDTO.getChunkNumber();// 当前分片大小 Long currentChunkSize = fileSliceDTO.getCurrentChunkSize();// 总分片数 Integer totalChunks = fileSliceDTO.getTotalChunks();// 原文件md5 String fileMd5 = fileSliceDTO.getFileMd5();// 文件名称 String fileName = fileSliceDTO.getFileName();// 用户账号 String userAccount = fileSliceDTO.getUserAccount();// 文件总大小 Long totalSize = fileSliceDTO.getTotalSize();// 当前分片文件流 MultipartFile mFile = fileSliceDTO.getFile();log.info("接收到文件:{},总大小:{} 总分片:{} 当前分片:{}", fileName, this.readableFileSize(totalSize), totalChunks, chunkNumber);String path = uploadPath + fileMd5 + "_" + userAccount + File.separator;File dirfile = new File(path);if (!dirfile.exists()) {dirfile.mkdirs();}String currentChunkFileName = path + fileMd5 + "_" + chunkNumber + ".tmp";File file = new File(currentChunkFileName);boolean uploadFlag = this.fileUpload(mFile, file);if (uploadFlag) {if (chunkNumber < totalChunks) {return true;} else {// 如果是最后一个分片,上传成功后就进行文件合并String mergeFileName = null;try {mergeFileName = this.fileMerge(fileMd5, totalChunks, fileName, totalSize, userAccount);} catch (IOException e) {log.info("文件合并发生异常:{}, {}", fileName, e.toString());}if (StringUtils.isBlank(mergeFileName)) {// 合并失败后异步删除文件夹CompletableFuture.runAsync(() - > { delFile(new File(path)); });return false;}log.info("文件合并成功:{}, 总大小: {}", fileName, this.readableFileSize(totalSize)); // 合并成功后异步删除文件夹CompletableFuture.runAsync(() - > {delFile(new File(path)); });return true;}}return false;}/** * @description: 文件上传私有方法* @date: 2024/3/12 15:25 * @param multipartFile 分片文件* @param file 目标文件* @return boolean */private boolean fileUpload(MultipartFile multipartFile, File file) {boolean flag = false;try {if (!file.exists()) {file.createNewFile();multipartFile.transferTo(file);} else {log.info("当前分片文件已存在:{}", file.getName());}flag = true;} catch (Exception e) {log.info("文件上传失败:{}, {}", multipartFile.getOriginalFilename(), e.toString());}return flag;} /** * @description: 合并文件 * @date: 2024/3/12 15:21 * @param fileMd5 * @param chunks * @param fileName * @param totalSize * @param userAccount * @return java.lang.String */private String fileMerge(String fileMd5, Integer chunks, String fileName, long totalSize, String userAccount) throws IOException {String fileType = this.getFileSuffix(fileName);String mergePath = uploadPath + UUIDUtil.uuid32() + "." + fileType;FileOutputStream fileOutputStream = new FileOutputStream(mergePath);String mergeFileName = null;long fileSize = 0 L;try {byte[] buf = new byte[1024 * 4];for (int i = 1; i <= chunks; i++) {String chunkFile = i + ".tmp";File file = new File(uploadPath + fileMd5 + "_" + userAccount + File.separator + fileMd5 + "_" + chunkFile);fileSize = fileSize + file.length();InputStream inputStream = new FileInputStream(file);int len;while ((len = inputStream.read(buf)) != -1) {fileOutputStream.write(buf, 0, len);}inputStream.close();}mergeFileName = mergePath;} catch (Exception e) {log.info("合并文件失败:{},{}", fileName, e.toString());} finally {fileOutputStream.close();}if (fileSize != totalSize) {log.info("文件总大小不一致:{}", fileName);return null;}return mergeFileName;} /*** @description: 删除文件/文件夹 * @date: 2024/3/12 15:19* @param file * @return boolean*/private boolean delFile(File file) {if (file == null || !file.exists()) {return true;}if (!file.isDirectory()) {file.delete();return true;} else {File[] files = file.listFiles();for (File f: files) {if (f.isDirectory()) {delFile(f);} else {f.delete();}}file.delete();return true;}} /** * @description: 文件大小可读化转换* @date: 2024/3/12 15:18 * @param size* @return java.lang.String */private String readableFileSize(long size) {if (size <= 0) {return "0";}final String[] units = new String[] {"B", "KB", "MB", "GB", "TB"};int digitGroups = (int)(Math.log10(size) / Math.log10(1024));return new DecimalFormat("#,###.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];} /** * @description: 获取文件的拓展名 支持形式 abc.jpg or D:/data/abc.txt; * @date: 2024/3/12 15:16 * @param fileName * @return java.lang.String */private String getFileSuffix(String fileName) {String newFileName = fileName; if(fileName.contains("/") || fileName.contains("\\")){ String replaceFileName = fileName.replaceAll("\\\\", "/"); int lastIndexOf = replaceFileName.lastIndexOf("/"); if(lastIndexOf > 0) { newFileName = replaceFileName.substring(lastIndexOf); } } int lastIndexOf = newFileName.lastIndexOf(".");String fileSuffix = lastIndexOf >= 0 ? newFileName.substring(lastIndexOf + 1).toLowerCase() : "";fileSuffix = newFileName.toLowerCase().endsWith(".tar.gz") ? "tar.gz" : fileSuffix;return fileSuffix;}
}
相关文章:
java实现文件分片上传并且断点续传
文章目录 什么是断点续传后端实现JAVA实现大文件分片上传断点续传 什么是断点续传 用户上传大文件,网络差点的需要历时数小时,万一线路中断,不具备断点续传的服务器就只能从头重传,而断点续传就是,允许用户从上传断线的地方继续传…...
leetcode hot100 之【LeetCode 15. 三数之和】 java实现
LeetCode 15. 三数之和 题目描述 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c 使得 a b c 0?请你找出所有和为 0 且不重复的三元组。 注意: 答案中的三元组可以按任意顺序组织。在 n…...
mysql学习教程,从入门到精通,sql序列使用(45)
sql序列使用 在SQL中,序列(Sequence)是一种数据库对象,用于生成唯一的数值,通常用于自动递增的主键。不同的数据库管理系统(DBMS)对序列的支持和语法可能有所不同。以下是一些常见的DBMS&#…...
Java 中的异常处理、常见异常、如何自定义异常类、Checked 和 Unchecked 异常的区别、如何处理数据库事务中的异常
文章目录 1. 异常的基本概念与处理方法定义常见异常类补充说明: 异常处理方法示例 2.如何自定义异常类步骤示例 3. Java 中的 Checked 和 Unchecked 异常的区别Checked 异常Unchecked 异常示例 4. 如何处理数据库事务中的异常常见场景处理方式示例讨论 总结 异常是指…...
6.1 特征值介绍
一、特征值和特征向量介绍 本章会开启线性代数的新内容。前面的第一部分是关于 A x b A\boldsymbol x\boldsymbol b Axb:平衡、均衡和稳定状态;现在的第二部分是关于变化的。时间会加入进来 —— 连续时间的微分方程 d u / d t A u \pmb{\textrm{d}…...
Vue01
前端最新Vue2Vue3基础入门到实战项目全套教程,自学前端vue就选黑马程序员,一套全通关!_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1HV4y1a7n4?spm_id_from333.788.videopod.episodes&vd_source016213ecd945408976ff307a6bda30…...
MySQL - Navicat自动备份MySQL数据
对于从事IT开发的工程师,数据备份我想大家并不陌生,这件工程太重要了!对于比较重要的数据,我们希望能定期备份,每天备份1次或多次,或者是每周备份1次或多次。 如果大家在平时使用Navicat操作数据库&#x…...
系统分析师20:【案例特训专题3】系统设计与运维
1 Web开发 1.1 Web开发涉及技术的综合应用 高性能高可用可维护应变安全 1.2 Web系统架构演化过程 1.2.1 单台机器到数据库与Web服务器分离 早期的web系统往往以单台机器形态出现,web网站无论是前端还是后台数据库都部署在一台服务器上,部署起来比较…...
Linux 局域网中使用NTP配置时间服务
一:NTP 时间服务器配置 前提: 局域网环境中一般不能直接使用互联网上提供的时间服务器,例如ntp.aliyun.com。所以可以使用局域网中的一个服务器时间为基准,其他服务器的时间都和他保持一致。 1、将服务器的系统时间配置为时间源…...
Shiro会话管理和加密
一、会话相关API及会话使用 Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如Web容器Tomcat),可以在JavaSE和JavaEE环境中使用。会话相关API主要包括: Subject.getSession(): 获取当前用户的会话࿰…...
GPON、XG-PON和XGS-PON的区别
类别GPON10G PON 细分 GPON XG-PON XGS-PON 下行速率 2.488 Gbps 9.953 Gbps 9.953Gbps 上行速率 1.244 Gbps 2.488 Gbps 9.953Gbps 可用带宽 2200Mbps 8500Mbps 8500Mbps 1000Mbps2000Mbps8500Mbps ITU-T标准 G.984(2003年) G.987 &a…...
Spring 项目返回值枚举类编写技巧
Spring 项目返回值枚举类编写技巧 在 Spring 项目中,使用枚举类来统一管理返回值和状态码是一种非常优雅的实现方式。这不仅能提升代码的可读性和维护性,还能避免在代码中硬编码字符串或数字来表示状态码。本文将以 ReturnCodeEnum 为例,介绍…...
【操作系统】06.进程控制
一、进程创建 1.1 认识fork函数 在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。 进程调用fork,当控制转移到内核中的fork代码后,内核将 分配新的内存块和内核数据结构…...
16天自制CppServer-day02
day02-设置错误与异常处理机制 上一天我们写了一个客户端与服务器通过socket进行连接,对socket,bind,listen,accept,connect等函数,我们都设想程序完美地、没有任何异常地运行,但显然这不现实,应该设置出现异常的处理机制&#x…...
时空智友企业流程化管控系统uploadStudioFile接口存在任意文件上传漏洞
免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 1. 时空智友…...
Linux 中文件的权限说明
目录 一:文件权限类型二:默认权限管理1. 查看当前用户的umask值2. 修改当前用户的umask值3. 根据umask计算默认权限 三:普通权限管理1. 三种普通权限说明1.1 对于非目录文件来说1.2 对于目录文件来说 2. 查看某个文件的权限信息2.1 使用 ls -…...
MySql数据库中数据类型
本篇将介绍在 MySql 中的所有数据类型,其中主要分为四类:数值类型、文本和二进制类型、时间日期、String 类型。如下(图片来源:MySQL数据库): 目录如下: 目录 数值类型 1. 整数类型 2. …...
Godot中的信号
目录 概念 signal connect方法连接Callable 信号要求参数 查看信号 连接信号 监听信号 Button - text属性 pressed 连接源 「按钮」的信号连接 使用代码,将方法与信号相连接 节点的connect方法 节点直接使用emit_signal方法通过字符串的方式触发信号…...
vba学习系列(8)--指定列单元格时间按时间段计数
系列文章目录 文章目录 系列文章目录前言一、背景二、VBA总结 前言 一、背景 时间格式:00:00:00 时间段格式:00:00:00 - 01:00:00 计数N列单元格时间位于时间段内的行数 二、VBA 代码如下(示例): Sub AssignTimeSeg…...
大型企业软件开发是什么样子的? - Web Dev Cody
引用自大型企业软件开发是什么样子的? - Web Dev Cody_哔哩哔哩_bilibili 一般来说 学技术的时候 我们会关注 开发语言特性 ,各种高级语法糖,底层技术 但是很少有关注到企业里面的开发流程,本着以终为始(以就业为导向…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
