当前位置: 首页 > news >正文

文件上传——springboot大文件分片多线程上传功能,前端显示弹出上传进度框

 一、项目搭建

  1. 创建 Spring Boot 项目: 创建一个新的 Spring Boot 项目,添加 Web 依赖。

  2. 添加依赖: 在 pom.xml 文件中添加以下依赖:

<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.4</version>
</dependency>
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version>
</dependency>

二、后端实现

  1. 配置 MultipartResolver: 在 Spring Boot 配置类中添加以下代码:

@Configuration
public class MyWebAppConfigurer implements WebMvcConfigurer {@Beanpublic MultipartResolver multipartResolver() {CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();multipartResolver.setMaxUploadSize(-1); // 设置最大上传大小,-1 表示无限制return multipartResolver;}
}
  1. 创建 FileUploadService: 创建一个服务类,用于处理文件上传逻辑:

@Service
public class FileUploadService {private String uploadDir = "upload/"; // 设置上传目录public String initUpload(String fileName, long fileSize, int chunkSize) {// 1. 生成任务 ID (UUID)String fileId = UUID.randomUUID().toString();//这里根据实际情况考虑到断点续传的功能,同一个文件生成的标识要一样,后期可以根据这个判断文件上传进度// 2. 创建临时目录: uploadDir/fileIdFile dir = new File(uploadDir, fileId);if (!dir.exists()) {dir.mkdirs();}// 3. 返回 fileIdreturn fileId;}public String uploadChunk(String fileId, int chunkIndex, int totalChunks, MultipartFile file) throws IOException {String fileUrl="";// 1. 获取分片文件String fileName = file.getOriginalFilename();// 2. 保存分片到临时目录: uploadDir/fileId/chunkIndexFile chunkFile = new File(uploadDir, fileId + "/" + chunkIndex);file.transferTo(chunkFile);// 检查所有分片是否已上传完成if (allChunksUploaded(fileId, totalChunks)) {String fileName=datePath()+"/"+fileId;//这里可以不用文件名,如果需要用,则要在controller上传请求增加一个fileName参数// 合并分片fileUrl = mergeChunks(fileId, fileName);}// 3. 校验分片 MD5 (可选)return fileUrl;}//判断所有的分片是否都上传完毕private boolean allChunksUploaded(String fileId, int totalChunks) {for (int i = 0; i < totalChunks; i++) {File chunkFile = new File(uploadDir + fileName + ".chunk" + i);if (!chunkFile.exists()) {return false;}}return true;}public String mergeChunks(String fileId, String fileName) throws IOException {// 1. 获取所有分片文件File dir = new File(uploadDir, fileId);File[] chunkFiles = dir.listFiles();// 2. 按顺序合并分片File mergedFile = new File(uploadDir, fileName);try (FileOutputStream fos = new FileOutputStream(mergedFile, true)) {for (File chunkFile : chunkFiles) {try (FileInputStream fis = new FileInputStream(chunkFile)) {IOUtils.copy(fis, fos);}}}// 3. 删除临时目录FileUtils.deleteDirectory(dir);// 4. 校验文件 MD5 (可选)// 5. 返回文件存储路径return uploadDir + fileName;}/*** 日期路径 即年/月/日 如2018/08/08*/public static final String datePath(){Date now = new Date();return DateFormatUtils.format(now, "yyyy/MM/dd");}
}
  1. 创建 FileUploadController: 创建一个控制器类,用于处理文件上传请求:

        要在分片上传的基础上实现断点续传,需要在服务端记录每个文件的上传进度,并在客户端请求上传时返回已上传的分片信息。

  • 使用数据库或其他存储机制记录每个文件的上传进度。
  • 可以使用以下信息标识一个上传任务:
    • fileId: 全局唯一标识符,例如 UUID
    • 同一个文件的标识是一样的,这样保证接这上次的进度继续上传。

        

@RestController
public class FileUploadController {@Autowiredprivate FileUploadService fileUploadService;private final ExecutorService executorService = Executors.newFixedThreadPool(5); // 线程池大小可配置private final Map<String, Set<Integer>> uploadProgress = new HashMap<>(); // 使用内存存储上传进度,实际应用中建议使用数据库@PostMapping("/upload/init")public ResponseEntity<String> initUpload(@RequestParam("fileName") String fileName,@RequestParam("fileSize") long fileSize,@RequestParam("chunkSize") int chunkSize) {String fileId = fileUploadService.initUpload(fileName, fileSize, chunkSize);return ResponseEntity.ok(fileId);}@PostMapping("/upload/chunk")public ResponseEntity<Void> uploadChunk(@RequestParam("fileId") String fileId,@RequestParam("chunkIndex") int chunkIndex, @RequestParam("totalChunks") int totalChunks,@RequestParam("file") MultipartFile file) throws IOException {String filePath = "";try {// 获取或创建上传进度记录Set<Integer> uploadedChunks = uploadProgress.computeIfAbsent(fileId, k -> new HashSet<>());// 如果分片已上传,则跳过if (uploadedChunks.contains(chunkIndex)) {return ResponseEntity.ok(new UploadResponse(uploadedChunks));}// 使用线程池处理每个分片上传executorService.execute(() -> {try {filePath = fileUploadService.uploadChunk(fileId, chunkIndex, totalChunks, file);} catch (IOException e) {// 处理异常,例如记录日志或返回错误信息e.printStackTrace();}});return ResponseEntity.status(HttpStatus.ACCEPTED).body(new UploadResponse(uploadedChunks, filePath));} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error uploading chunk.");}//return ResponseEntity.ok().build();}}// 用于响应上传请求,携带已上传分片信息
class UploadResponse {Set<Integer> uploadedChunks;String filePath;public UploadResponse(Set<Integer> uploadedChunks, String filePath) {this.uploadedChunks = uploadedChunks;this.filePath = filePath;}public UploadResponse(Set<Integer> uploadedChunks) {this.uploadedChunks = uploadedChunks;}// ... getter setter 方法 ...
}

三、前端实现

  1. HTML 页面: 创建一个简单的 HTML 页面,包含文件选择按钮、上传进度条和相关信息展示区域。

  2. JavaScript 代码: 使用 JavaScript 实现文件分割、分片上传、合并请求和上传进度展示等功能。

// 选择文件
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', (event) => {const file = event.target.files[0];// ... 文件分割、上传逻辑
});// 文件分割
const chunkSize = 4 * 1024 * 1024; // 4MB
const chunks = sliceFile(file, chunkSize);const totalChunks = Math.ceil(file.size / chunkSize);// 初始化上传
const fileId = await initUpload(file.name, file.size, chunkSize);// 并发上传分片
const uploadPromises = chunks.map((chunk, index) => uploadChunk(fileId, index, totalChunks, chunk)
);// 上传完成合并得到文件url
await Promise.all(uploadPromises);async function uploadChunk(fileId, chunkIndex, totalChunks, chunk) {// ... 创建 FormData ...const formData = new FormData();formData.append('file', chunk);formData.append('chunkIndex', chunkIndex);formData.append('totalChunks', totalChunks);formData.append('fileId', fileId); // 添加 fileId 参数const response = await fetch('/upload', {method: 'POST',body: formData,});//...处理响应数据
}function sliceFile(file, chunkSize) {let chunks = [];let count = Math.ceil(file.size / chunkSize);for (let i = 0; i < count; i++) {let offset = i * chunkSize;let chunk = file.slice(offset, offset + chunkSize + 1);chunks.push(chunk);}return chunks;
}

四、上传进度展示

  1. 后端: 在 FileUploadService 中添加方法,根据 fileId 返回已上传分片数量或计算上传进度百分比。

  2. 前端: 使用 setInterval 定时请求后端获取上传进度,并更新进度条。

前端html代码使用示例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>文件上传</title>
</head>
<body>
<h1>大文件分片上传</h1>
<input type="file" id="fileInput">
<button id="uploadBtn">上传</button>
<div>上传进度:<progress id="progressBar" value="0" max="100"></progress> <span id="progressText">0%</span></div>
<script>const fileInput = document.getElementById('fileInput');const uploadBtn = document.getElementById('uploadBtn');const progressBar = document.getElementById('progressBar');const progressText = document.getElementById('progressText');uploadBtn.addEventListener('click', uploadFile);async function uploadFile() {const file = fileInput.files[0];if (!file) {alert('请选择文件');return;}const chunkSize = 4 * 1024 * 1024; // 4MBconst fileId = await initUpload(file.name, file.size, chunkSize);const chunks = sliceFile(file, chunkSize);let uploadedChunks = 0;const uploadPromises = chunks.map((chunk, index) => {return uploadChunk(fileId, index, chunks.length, chunk).then(() => {uploadedChunks++;updateProgress(uploadedChunks / chunks.length);});});await Promise.all(uploadPromises);await mergeChunks(fileId);alert('上传完成!');}function sliceFile(file, chunkSize) {const chunks = [];let offset = 0;while (offset < file.size) {chunks.push(file.slice(offset, offset + chunkSize));offset += chunkSize;}return chunks;}async function initUpload(fileName, fileSize, chunkSize) {const response = await fetch('/upload/init', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ fileName, fileSize, chunkSize })});return await response.text();}async function uploadChunk(fileId, chunkIndex, totalChunks, chunk) {const formData = new FormData();formData.append('fileId', fileId);formData.append('chunkIndex', chunkIndex);formData.append('totalChunks', totalChunks);formData.append('file', chunk);await fetch('/upload/chunk', {method: 'POST',body: formData});}async function mergeChunks(fileId) {await fetch(`/upload/merge?fileId=${fileId}`);}function updateProgress(progress) {progressBar.value = progress * 100;progressText.textContent = Math.round(progress * 100) + '%';}
</script>
</body>
</html>

五、存储上传进度

使用 Redis 存储上传进度

  1. Redis 数据结构:

    • 使用 Hash 结构存储每个文件的上传进度,key 为 fileId和chunkIndex,chunkIndex 为分片索引,value 为 true 或 false,表示分片是否已上传。
  2. 代码实现:

  • 注入 RedisTemplate:
@Autowired
private RedisTemplate<String, String> redisTemplate;
  • 修改 uploadChunk 方法: 
private void uploadChunk(String fileId, int chunkIndex, int totalChunks, MultipartFile file) throws IOException {// ... 保存分片文件 ...// 更新上传进度到 RedisString chunkKey = fileId + ":" + chunkIndex;redisTemplate.opsForValue().set(chunkKey, "true");// 检查所有分片是否已上传完成if (redisTemplate.opsForHash().size(fileId) == totalChunks) {// ... 合并分片 ...// 清除上传进度redisTemplate.delete(fileId);}
}
  • 添加 /upload/progress 接口: 
@GetMapping("/upload/progress")
public ResponseEntity<UploadResponse> getUploadProgress(@RequestParam("identifier") String identifier
) {Set<String> uploadedChunks = redisTemplate.keys(identifier + ":*");Set<Integer> uploadedChunkIndices = uploadedChunks.stream().map(s -> Integer.parseInt(s.substring((identifier + ":").length()))).collect(Collectors.toSet());return ResponseEntity.ok(new UploadResponse(uploadedChunkIndices));
}
  • 前端js使用
// ... 其他代码 ...
// 选择文件
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', (event) => {const file = event.target.files[0];// ... 文件分割、上传逻辑
});// 文件分割
const chunkSize = 4 * 1024 * 1024; // 4MB
const chunks = sliceFile(file, chunkSize);
const totalChunks = Math.ceil(file.size / chunkSize);// 初始化上传
const fileId = await initUpload(file.name, file.size, chunkSize);async function uploadFile(chunks) {// 获取已上传的分片信息const uploadedChunks = await getUploadedChunks(fileId, file.name);// ... 根据 uploadedChunks 调整分片上传逻辑 ...if(...){//获取uploadedChunks为分片的索引,表示当前文件上传的进度,根据uploadedChunks的具体数据类型去取for (let i = (uploadedChunks); i < chunks.length; i++) {//const start = i * chunkSize;//const end = Math.min(start + chunkSize, file.size);//const chunk = file.slice(start, end);const chunk = chunks[i];await uploadChunk(fileId, i, totalChunks, chunk);}}else{// 并发上传分片chunks.map((chunk, index) => uploadChunk(fileId, index, totalChunks, chunk));}  
}async function getUploadedChunks(fileId, fileName) {const response = await fetch(`/upload/progress?identifier=${fileId}&fileName=${fileName}`);const data = await response.json();return data.uploadedChunks || [];
}async function uploadChunk(fileId, chunkIndex, totalChunks, chunk) {// ... 创建 FormData ...const formData = new FormData();formData.append('file', chunk);formData.append('chunkIndex', chunkIndex);formData.append('totalChunks', totalChunks);formData.append('fileId', fileId); // 添加 fileId 参数const response = await fetch('/upload', {method: 'POST',body: formData,});//...处理响应数据
}function sliceFile(file, chunkSize) {let chunks = [];let count = Math.ceil(file.size / chunkSize);for (let i = 0; i < count; i++) {let offset = i * chunkSize;let chunk = file.slice(offset, offset + chunkSize + 1);chunks.push(chunk);}return chunks;
}

 重新上传获取进度:

  • 用户重新选择同一个文件上传时,需要生成相同的 fileId
  • 在前端上传前,调用 /upload/progress 接口,传入 fileId 获取已上传的分片信息。
  • 根据返回的已上传分片信息,跳过已上传的分片,继续上传剩余分片。

六、注意事项

  • 以上代码示例省略了部分细节,例如异常处理、MD5 校验等,请根据实际情况进行完善。
  • 前端代码需要根据您使用的 JavaScript 框架进行调整。
  • 建议您先学习 Spring Boot 文件上传、JavaScript 文件操作和 AJAX 等前端相关知识。

希望这些更详细的步骤和代码片段能够帮助您更好地理解和实现 Spring Boot 断点续传、多线程分片上传功能! 如果您还有其他问题,请随时提出。

相关文章:

文件上传——springboot大文件分片多线程上传功能,前端显示弹出上传进度框

一、项目搭建 创建 Spring Boot 项目: 创建一个新的 Spring Boot 项目&#xff0c;添加 Web 依赖。 添加依赖: 在 pom.xml 文件中添加以下依赖: <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId&…...

每日学术速递8.2

1.A Scalable Quantum Non-local Neural Network for Image Classification 标题&#xff1a; 用于图像分类的可扩展量子非局部神经网络 作者&#xff1a; Sparsh Gupta, Debanjan Konar, Vaneet Aggarwal 文章链接&#xff1a;https://arxiv.org/abs/2407.18906 摘要&#x…...

SAP-PLM创建物料主数据接口

FUNCTION zplm_d_0001_mm01. *"---------------------------------------------------------------------- *"*"本地接口&#xff1a; *" EXPORTING *" VALUE(EX_TOTAL) TYPE CHAR4 *" VALUE(EX_SUCCESSFUL) TYPE CHAR4 *" …...

超声波眼镜清洗机哪个品牌好?四款高性能超声波清洗机测评剖析

对于追求高生活质量的用户来说&#xff0c;眼镜的清洁绝对不能马虎。如果不定期清洁眼镜&#xff0c;时间久了&#xff0c;镜片的缝隙中会积累大量的灰尘和细菌&#xff0c;眼镜靠近眼部&#xff0c;对眼部健康有很大影响。在这种情况下&#xff0c;超声波清洗机显得尤为重要。…...

卸载Windows软件的正确姿势,你做对了吗?

前言 今天有小伙伴突然问我&#xff1a;她把软件都卸载了&#xff0c;但是怎么软件都还在运行&#xff1f; 这个问题估计很多小伙伴都是遇到过的&#xff0c;对于电脑小白来说&#xff0c;卸载Windows软件真的真的真的是一件很难的事情。所以&#xff0c;今天咱们就来讲讲&am…...

WEB前端14-Element UI(学生查询表案例/模糊查询/分页查询)

Vue2-Element UI 1.可重用组件的开发 可重用组件 我们一般将可重复使用的组件放在components目录之下&#xff0c;以便父组件的灵活调用 <!--可重用组件一般与css密切相关&#xff0c;使用可重用组件的目的是&#xff0c;将相似的组件放在一起&#xff0c;方便使用-->…...

使用swiftui自定义圆形进度条实现loading

实现的代码如下&#xff1a; // // LoadingView.swift // SwiftBook // // Created by Song on 2024/8/2. //import SwiftUIstruct LoadingView: View {State var process 0.5var body: some View {VStack(spacing: 20) {ZStack {Circle().stroke(.gray.opacity(0.3), lin…...

C# 设计模式之抽象工厂模式

总目录 前言 工厂方法模式是为了克服简单工厂模式的缺点而设计出来的&#xff0c;简单工厂模式的工厂类随着产品类的增加需要增加额外的代码&#xff0c;而工厂方法模式每个具体工厂类只完成单个实例的创建&#xff0c;所以它具有很好的可扩展性。但是在现实生活中&#xff0c…...

Javascript前端面试基础(八)

window.onload和$(document).ready区别 window.onload()方法是必须等到页面内包括图片的所有元素加载完毕后才能执行$(document).ready()是DOM结构绘制完毕后就执行&#xff0c;不必等到加载完毕 window.onload 触发时机&#xff1a;window.onload 事件会在整个页面&#xf…...

R 语言学习教程,从入门到精通,R的安装与环境的配置(2)

1、R的安装与环境的配置 R语言是一款完全免费且开源的软件&#xff0c;它的开源许可证是GNU通用公共许可证&#xff08;GPL&#xff09;&#xff0c;这意味着任何人都可以自由地使用、复制、修改和发布R语言的源代码&#xff0c;甚至可以将其用于商业用途。 和python等其他语言…...

Python批量下载音乐功能

Python批量下载音乐功能 Python批量下载音乐,调用API接口,同时下载歌曲和歌词 先安排一下要用的模块&#xff0c;导入进来。 import re import json import requests目录结构 下载音乐 Awking_Class.pymusic.txt 文件文件写的是音乐名字,使用换行分割 new_music 注意这个 ne…...

用 Bytebase 实现批量、多环境、多租户数据库的丝滑变更

Bytebase 提供了多种功能来简化批量变更管理&#xff0c;适用于多环境或多租户情况。本教程将指导您如何使用 部署配置 和 数据库组 在不同场景下进行数据库批量变更。 默认流水线 vs 部署配置 图片数据库 vs 数据库组 1. 准备 请确保已安装 Docker&#xff0c;如果本地没有重…...

java之方法引用 —— ::

目录 一、简介 二、引用静态方法 1.格式 2.示例 ​编辑 3.条件解析 三、引用成员方法 1.格式 2.示例 四、引用构造方法 1.格式 2.示例 五、类名引用成员方法 1.格式 2.略微不同的方法引用规则 3.示例 六、引用数组的构造方法 1.格式 2.示例 一、简介 方…...

「测试线排查的一些经验-上篇」 后端工程师

文章目录 端口占用脚本失灵线上部署项目结构模版配置文件生效 一般产品研发过程所使用的环境可分为&#xff1a; 研发环境-dev测试环境-test生产环境-prod 软件开发中&#xff0c;完整测试环境包括&#xff1a;UT、IT、ST、UAT UT Unit Test 单元测试 IT System Integration …...

AOSP12_BatteryStats统计电池数据信息

前言 BatteryStats模块主要用于设备在电池供电是系统对各个模块电量使用的统计,Android提供的Battery Historain工具就是对此模块统计的数据进行解析和展示。 一 BatteryStats模块类图 模块主要类图如下:见根目录的模块类图 BatteryStats:抽象类,本模块的核心类,主要定…...

【Android Studio】UI 布局

文章目录 view布局LinearLayout view 在Android开发中&#xff0c;View是一个非常重要的概念&#xff0c;它是所有用户界面组件的基类。View类及其子类构成了Android应用中的用户界面。每个View都占用屏幕上的一个矩形区域&#xff0c;并可以响应用户输入&#xff08;如触摸、按…...

虚拟机Windows server忘记密码解决方法

原理 utilman.exe是Windows辅助工具管理器程序&#xff0c;‌虽然它本身不是一个关键的系统进程&#xff0c;‌但通过修改这个文件&#xff0c;‌用户可以访问一些有用的UI设置。‌在某些情况下&#xff0c;‌比如忘记密码需要重置时&#xff0c;‌通过修改utilman.exe文件为c…...

【香橙派系列教程】(六)嵌入式SQLite数据库

【六】嵌入式SQLite数据库 文章目录 【六】嵌入式SQLite数据库1.简介2.SQLite数据库安装3.SQLite命令用法1.创建数据库2.创建和查看表格3.插入查看数据&#xff08;记录&#xff09;4.删除更改数据&#xff08;记录&#xff09; 4.SQLite编程操作1.打开/创建数据库的C接口2.创建…...

深入探讨PHP8的新特性与性能优化

本文由 ChatMoney团队出品 随着互联网技术的飞速发展&#xff0c;PHP作为后端开发领域的热门语言也在不断演进。近期&#xff0c;PHP8的发布引起了广泛关注。本文将为您详细介绍PHP8的新特性以及性能优化&#xff0c;并通过具体示例帮助您更好地理解和应用这些新特性。 一、PH…...

2024年06月 Scratch 图形化(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch图形化等级考试(1~4级)全部真题・点这里 一、单选题(共10题,共30分) 第1题 运行下列程序,输入单词“PLAY”,最后角色说?( ) A:LY4AP B:AP4LY C:YA4PL D:PL4AY 答案:B 根据程序分析可知,首先获取单词字符数,然后奇数位的字母放在字符数左侧,偶数位…...

RMBG-2.0抠图工具性能测试:RTX 3060上平均1.8秒处理一张图

RMBG-2.0抠图工具性能测试&#xff1a;RTX 3060上平均1.8秒处理一张图 1. 为什么选择本地抠图工具&#xff1f; 在日常工作和内容创作中&#xff0c;我们经常需要处理图片背景去除的需求。无论是电商产品图、社交媒体配图还是设计素材&#xff0c;一个高效的抠图工具可以大幅…...

SAM 3入门到应用:从图片分割到视频跟踪完整指南

SAM 3入门到应用&#xff1a;从图片分割到视频跟踪完整指南 1. SAM 3简介与核心能力 SAM 3&#xff08;Segment Anything Model 3&#xff09;是Facebook推出的新一代图像和视频分割模型&#xff0c;它通过统一的基础架构实现了前所未有的通用分割能力。与传统的专用分割模型…...

Pixel Dream Workshop 算法原理浅析:从扩散模型到创意生成

Pixel Dream Workshop 算法原理浅析&#xff1a;从扩散模型到创意生成 1. 引言&#xff1a;理解扩散模型的价值 最近两年&#xff0c;扩散模型在图像生成领域掀起了一场革命。从最初的DALLE到Stable Diffusion&#xff0c;再到各种创意生成工具&#xff0c;这项技术正在改变我…...

Qwen-Image-Edit-2509镜像部署实战:跟着图文教程,10分钟跑通AI修图

Qwen-Image-Edit-2509镜像部署实战&#xff1a;跟着图文教程&#xff0c;10分钟跑通AI修图 1. 快速了解Qwen-Image-Edit-2509 Qwen-Image-Edit-2509是阿里巴巴通义千问团队推出的最新AI图像编辑工具。这个模型最大的特点是能够理解自然语言指令&#xff0c;对图片进行智能修改…...

OWL ADVENTURE助力在线教育:AI自动批改绘图作业实践

OWL ADVENTURE助力在线教育&#xff1a;AI自动批改绘图作业实践 想象一下&#xff0c;一位在线美术老师&#xff0c;面对上百份刚刚提交的手绘作业。他需要一份份打开&#xff0c;仔细查看学生的构图、线条、比例&#xff0c;然后写下针对性的评语。这个过程不仅耗时费力&…...

【Python内存管理2026权威白皮书】:GIL演进、引用计数重构与GC智能调度三大突破性策略首次公开

第一章&#xff1a;Python智能体内存管理策略2026最新趋势全景概览随着大语言模型驱动的Python智能体&#xff08;Agent&#xff09;在生产环境中的深度部署&#xff0c;传统CPython内存管理机制正面临前所未有的挑战&#xff1a;动态工具调用、多轮推理缓存、跨Agent状态共享及…...

Android Perfetto 系列 6:为什么是 120Hz?高刷新率的优势与挑战

Android Perfetto 系列 6&#xff1a;为什么是 120Hz&#xff1f;高刷新率的优势与挑战本文是 Android Perfetto 系列的第六篇&#xff0c;主要介绍 Android 设备上 120Hz 刷新率的相关知识。如今&#xff0c;120Hz 已成为 Android 旗舰手机的标配&#xff0c;本文将讨论高刷新…...

Python原生AOT编译到底稳不稳?我们压测了7类生产负载:高并发API、实时流处理、边缘AI推理——结果出乎意料(附完整benchmark报告)

第一章&#xff1a;Python原生AOT编译方案2026实战案例全景概览Python原生AOT&#xff08;Ahead-of-Time&#xff09;编译在2026年已进入工程化落地深水区&#xff0c;主流方案如Nuitka 2.0、PyO3 Rust AOT Pipeline、以及新兴的CPython官方实验分支cpython-aot&#xff0c;均…...

让ai成为你的vue开发搭档,用快马智能优化代码性能与结构

让AI成为你的Vue开发搭档&#xff0c;用快马智能优化代码性能与结构 最近在开发一个Vue3项目时&#xff0c;遇到了几个性能瓶颈问题。作为一个前端开发者&#xff0c;性能优化是绕不开的话题。幸运的是&#xff0c;借助AI辅助开发工具&#xff0c;这些问题都能得到更高效的解决…...

Stable Diffusion ComfyUI进阶:局部重绘与智能扩图的实战技巧与创意应用

1. 局部重绘的核心原理与实战技巧 局部重绘是Stable Diffusion ComfyUI中最实用的功能之一&#xff0c;它允许你在不改变整体构图的情况下&#xff0c;对图像的特定区域进行重新绘制。这个功能背后的技术原理其实很有意思——它利用了潜在空间&#xff08;latent space&#xf…...