18.将文件上传至云服务器 + 优化网站的性能
目录
1.将文件上传至云服务器
1.1 处理上传头像逻辑
1.1.1 客户端上传
1.1.2 服务器直传
2.优化网站的性能
2.1 本地缓存优化查询方法
2.2 压力测试
1.将文件上传至云服务器
- 客户端上传:客户端将数据提交给云服务器,并等待其响应;用户上传头像时,将表单数据提交给服务器
- 服务器直传:应用服务器将数据直接提交给云服务器,并等待其响应;分享时,服务端将自动生成的图片,直接提交给云服务器
使用七牛云服务器
导入依赖:
<!-- https://mvnrepository.com/artifact/com.qiniu/qiniu-java-sdk -->
<dependency><groupId>com.qiniu</groupId><artifactId>qiniu-java-sdk</artifactId><version>7.12.1</version>
</dependency>
在 application.properties 中配置:
# qiniu
qiniu.key.access=NHzlA2MRle10vB0wNeO54aGS-tMjEpO5BiIrflz9
qiniu.key.secret=qicgWpPOslm5_dFu_j_94r5gUcKm_UekhT2MMLPf
qiniu.bucket.header.name=communityheader123321
quniu.bucket.header.url=http://s6sc3za8f.hb-bkt.clouddn.com
qiniu.bucket.share.name=communityshare123321
qiniu.bucket.share.url=http://s6scgzhqs.hb-bkt.clouddn.com
1.1 处理上传头像逻辑
1.1.1 客户端上传
打开 UserController 类:
- 注入上述的两个 key
- 注入有关头像的空间内容
- 废弃原来上传头像方法:上传头像因为有表单,所以在客户端上传,表单直接提交给七牛云,废弃此方法
- 上传文件同样废弃
- 在打开用户设置的页面时需要生成凭证(设置上传文件名称,响应信息),将凭证写到表单中,当打开表单时,表单中应该有凭证
- 最后传给模板
- 还需要添加一个方法:在表单中将数据提交给七牛云,七牛云返回一个消息,然后将 User 表中的 headurl 做一个更新,更新为七牛云的路径
@Value("${qiniu.key.access}")private String accessKey;@Value("${qiniu.key.secret}")private String secretKey;@Value("${qiniu.bucket.header.name}")private String headerBucketName;@Value("${quniu.bucket.header.url}")private String headerBucketUrl;@LoginRequired@RequestMapping(path = "/setting", method = RequestMethod.GET)//添加方法使得浏览器通过方法访问到设置的页面public String getSettingPage(Model model) {// 上传文件名称String fileName = CommunityUtil.generateUUID();// 设置响应信息StringMap policy = new StringMap();policy.put("returnBody", CommunityUtil.getJSONString(0));// 生成上传凭证Auth auth = Auth.create(accessKey, secretKey);String uploadToken = auth.uploadToken(headerBucketName, fileName, 3600, policy);model.addAttribute("uploadToken", uploadToken);model.addAttribute("fileName", fileName);return "/site/setting";}// 更新头像路径@RequestMapping(path = "/header/url", method = RequestMethod.POST)@ResponseBodypublic String updateHeaderUrl(String fileName) {if (StringUtils.isBlank(fileName)) {return CommunityUtil.getJSONString(1, "文件名不能为空!");}String url = headerBucketUrl + "/" + fileName;userService.updateHeader(hostHolder.getUser().getId(), url);return CommunityUtil.getJSONString(0);}
再处理表单 setting.html:
<!-- 上传头像 --><h6 class="text-left text-info border-bottom pb-2">上传头像</h6><!--上传到本地--><!--<form class="mt-5" method="post" enctype="multipart/form-data" th:action="@{/user/upload}"><div class="form-group row mt-4"><label for="head-image" class="col-sm-2 col-form-label text-right">选择头像:</label><div class="col-sm-10"><div class="custom-file"><input type="file" th:class="|custom-file-input ${error!=null?'is-invalid':''}|"id="head-image" name="headerImage" lang="es" required=""><label class="custom-file-label" for="head-image" data-browse="文件">选择一张图片</label><div class="invalid-feedback" th:text="${error}">该账号不存在!</div></div></div></div><div class="form-group row mt-4"><div class="col-sm-2"></div><div class="col-sm-10 text-center"><button type="submit" class="btn btn-info text-white form-control">立即上传</button></div></div></form>--><!--上传到七牛云--><form class="mt-5" id="uploadForm"><div class="form-group row mt-4"><label for="head-image" class="col-sm-2 col-form-label text-right">选择头像:</label><div class="col-sm-10"><div class="custom-file"><input type="hidden" name="token" th:value="${uploadToken}"><input type="hidden" name="key" th:value="${fileName}"><input type="file" class="custom-file-input" id="head-image" name="file" lang="es" required=""><label class="custom-file-label" for="head-image" data-browse="文件">选择一张图片</label><div class="invalid-feedback">该账号不存在!</div></div></div></div><div class="form-group row mt-4"><div class="col-sm-2"></div><div class="col-sm-10 text-center"><button type="submit" class="btn btn-info text-white form-control">立即上传</button></div></div></form>
在 static 包下 js 包新建 setting.js:
$(function(){$("#uploadForm").submit(upload);
});function upload() {$.ajax({url: "http://upload-z1.qiniup.com",method: "post",processData: false,contentType: false,data: new FormData($("#uploadForm")[0]),success: function(data) {if(data && data.code == 0) {// 更新头像访问路径$.post(CONTEXT_PATH + "/user/header/url",{"fileName":$("input[name='key']").val()},function(data) {data = $.parseJSON(data);if(data.code == 0) {window.location.reload();} else {alert(data.msg);}});} else {alert("上传失败!");}}});return false;
}
1.1.2 服务器直传
需要重构分享相关的功能,打开 ShareController :
- 注入分享空间的 url
- 修改返回浏览器的路径:七牛云空间路径 = 空间 url + / + 图片名字
- 废弃从本地获取图片传给客户端的方法:传给七牛云之后,通过七牛云获取图片
@Value("${qiniu.bucket.share.url}")private String shareBucketUrl;@RequestMapping(path = "/share", method = RequestMethod.GET)@ResponseBodypublic String share(String htmlUrl) {// 文件名String fileName = CommunityUtil.generateUUID();// 异步生成长图Event event = new Event().setTopic(TOPIC_SHARE).setData("htmlUrl", htmlUrl).setData("fileName", fileName).setData("suffix", ".png");//触发事件eventProducer.fireEvent(event);// 返回访问路径Map<String, Object> map = new HashMap<>();// map.put("shareUrl", domain + contextPath + "/share/image/" + fileName);map.put("shareUrl", shareBucketUrl + "/" + fileName);return CommunityUtil.getJSONString(0, null, map);}// 废弃// 获取长图@RequestMapping(path = "/share/image/{fileName}", method = RequestMethod.GET)public void getShareImage(@PathVariable("fileName") String fileName, HttpServletResponse response) {if (StringUtils.isBlank(fileName)) {throw new IllegalArgumentException("文件名不能为空!");}response.setContentType("image/png");File file = new File(wkImageStorage + "/" + fileName + ".png");try {OutputStream os = response.getOutputStream();FileInputStream fis = new FileInputStream(file);byte[] buffer = new byte[1024];int b = 0;while ((b = fis.read(buffer)) != -1) {os.write(buffer, 0, b);}} catch (IOException e) {logger.error("获取长图失败: " + e.getMessage());}}
修改 EventConsumer,逻辑是在消费者中体现
- 在消费者中消费事件,传入两个 key 和上传空间的 name
- 注入 ThreadPoolTaskScheduler
- 在消费分享事件中,在执行生成图片时,启用定时器,监视该图片,一旦生成,则上传至七牛云.
// 消费分享事件@KafkaListener(topics = TOPIC_SHARE)public void handleShareMessage(ConsumerRecord record) {if (record == null || record.value() == null) {logger.error("消息的内容为空!");return;}Event event = JSONObject.parseObject(record.value().toString(), Event.class);if (event == null) {logger.error("消息格式错误!");return;}//获取 html、文件名、后缀String htmlUrl = (String) event.getData().get("htmlUrl");String fileName = (String) event.getData().get("fileName");String suffix = (String) event.getData().get("suffix");//拼接参数String cmd = wkImageCommand + " --quality 75 "+ htmlUrl + " " + wkImageStorage + "/" + fileName + suffix;//执行命令try {Runtime.getRuntime().exec(cmd);logger.info("生成长图成功: " + cmd);} catch (IOException e) {logger.error("生成长图失败: " + e.getMessage());}// 启用定时器,监视该图片,一旦生成了,则上传至七牛云.UploadTask task = new UploadTask(fileName, suffix);//触发定时器执行Future future = taskScheduler.scheduleAtFixedRate(task, 500);//完成任务后定时器关闭task.setFuture(future);}//相当于线程体class UploadTask implements Runnable {// 文件名称private String fileName;// 文件后缀private String suffix;// 启动任务的返回值private Future future;// 开始时间private long startTime;// 上传次数private int uploadTimes;public UploadTask(String fileName, String suffix) {this.fileName = fileName;this.suffix = suffix;this.startTime = System.currentTimeMillis();}public void setFuture(Future future) {this.future = future;}@Overridepublic void run() {// 生成失败if (System.currentTimeMillis() - startTime > 30000) {logger.error("执行时间过长,终止任务:" + fileName);future.cancel(true);return;}// 上传失败if (uploadTimes >= 3) {logger.error("上传次数过多,终止任务:" + fileName);future.cancel(true);return;}//没有上述情况继续执行相关逻辑//从本地中寻找文件String path = wkImageStorage + "/" + fileName + suffix;//本地路径File file = new File(path);if (file.exists()) {logger.info(String.format("开始第%d次上传[%s].", ++uploadTimes, fileName));// 设置响应信息StringMap policy = new StringMap();// 成功返回0policy.put("returnBody", CommunityUtil.getJSONString(0));// 生成上传凭证Auth auth = Auth.create(accessKey, secretKey);String uploadToken = auth.uploadToken(shareBucketName, fileName, 3600, policy);// 指定上传机房UploadManager manager = new UploadManager(new Configuration(Zone.zone1()));try {// 开始上传图片Response response = manager.put(path, fileName, uploadToken, null, "image/" + suffix, false);// 处理响应结果JSONObject json = JSONObject.parseObject(response.bodyString());if (json == null || json.get("code") == null || !json.get("code").toString().equals("0")) {logger.info(String.format("第%d次上传失败[%s].", uploadTimes, fileName));} else {logger.info(String.format("第%d次上传成功[%s].", uploadTimes, fileName));future.cancel(true);}} catch (QiniuException e) {logger.info(String.format("第%d次上传失败[%s].", uploadTimes, fileName));}} else {logger.info("等待图片生成[" + fileName + "].");}}}
2.优化网站的性能
本地缓存:将数据缓存在应用服务器上,性能最好;常用的缓存工具:Ehcache、Guava、Caffeine等
分布式缓存:将数据缓存在 NoSQL 数据库上,跨服务器;常用缓存工具:MemCache、Redis等
多级缓存:一级缓存 > 二级缓存 > DB;避免缓存雪崩(缓存失效,大量请求直达DB),提高系统的可用性
2.1 本地缓存优化查询方法
本地缓存主要使用 Caffeine 工具,导入依赖:
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.7.0</version></dependency>
设置自定义参数:
# caffeine
caffeine.posts.max-size=15
caffeine.posts.expire-seconds=180
优化查询方法(discussPostService):
- 初始化 Logger,记录日志
- 注入上述申明参数
- Caffeine核心接口: Cache, LoadingCache(同步缓存:多个线程同时访问缓存中的数据,但是缓存中没有数据,让多个线程排队等待,然后缓存去数据库中取), AsyncLoadingCache(异步缓存:支持并发同时取数据)
- 声明帖子列表缓存,使用 LoadingCache<String, List<DiscussPost>>(使用 key 缓存 value)
- 声明帖子总数缓存,使用 LoadingCache<Integer, Integer>
- 在服务启动或者首次调用 DiscussPostService,初始化一次上述两个缓存即可
- 给当前类新增初始化方法,添加注解 @PostConstruct,初始化帖子列表缓存和帖子总数缓存
- 在查询某一页的方法中需要启动缓存的条件:缓存热门帖子(orderMode == 1)并且缓存首页(访问首页的时候,userId 是不传为0),缓存的是一页的数据(key 为 offset 和 limit 组合);否则访问数据库,在访问之前记录一下日志(load post list from DB.)
- 在查询总数的方法中:用户查看自己帖子的时候不需要缓存,但是当 userId == 0 为首页查询,需要缓存帖子总数
private static final Logger logger = LoggerFactory.getLogger(DiscussPostService.class);@Value("${caffeine.posts.max-size}")private int maxSize;@Value("${caffeine.posts.expire-seconds}")private int expireSeconds;// Caffeine核心接口: Cache, LoadingCache, AsyncLoadingCache// 帖子列表缓存private LoadingCache<String, List<DiscussPost>> postListCache;// 帖子总数缓存private LoadingCache<Integer, Integer> postRowsCache;@PostConstructpublic void init() {// 初始化帖子列表缓存postListCache = Caffeine.newBuilder().maximumSize(maxSize)//缓存最大数据量.expireAfterWrite(expireSeconds, TimeUnit.SECONDS)//把缓存写入缓存空间多长时间自动过期.build(new CacheLoader<String, List<DiscussPost>>() {//当尝试从缓存中取数据,caffeine会看缓存是否有数据://没有,需要提供一个查询的方法,load就是实现这个查询方法@Nullable@Overridepublic List<DiscussPost> load(@NonNull String key) throws Exception {//实现访问数据库查数据if (key == null || key.length() == 0) {throw new IllegalArgumentException("参数错误!");}//不为空,解析 key(offset + ":" + limit),:进行切割String[] params = key.split(":");if (params == null || params.length != 2) {throw new IllegalArgumentException("参数错误!");}int offset = Integer.valueOf(params[0]);int limit = Integer.valueOf(params[1]);// 二级缓存: Redis -> mysqllogger.debug("load post list from DB.");//调用discussPostMapper查询数据(userId = 0 orderMode = 1)return discussPostMapper.selectDiscussPosts(0, offset, limit, 1);}});// 初始化帖子总数缓存postRowsCache = Caffeine.newBuilder().maximumSize(maxSize).expireAfterWrite(expireSeconds, TimeUnit.SECONDS).build(new CacheLoader<Integer, Integer>() {@Nullable@Overridepublic Integer load(@NonNull Integer key) throws Exception {logger.debug("load post rows from DB.");return discussPostMapper.selectDiscussPostRows(key);}});}//声明一个业务方法:查询某一页的方法,返回类型是集合public List<DiscussPost> findDiscussPosts(int userId, int offset, int limit, int orderMode) {//缓存热门帖子(orderMode == 1)并且缓存首页(访问首页的时候,userId 是不传为0)// 缓存的是一页的数据(key 为 offset 和 limit 组合)if (userId == 0 && orderMode == 1) {return postListCache.get(offset + ":" + limit);}//否则访问数据库,在访问之前记录一下日志logger.debug("load post list from DB.");return discussPostMapper.selectDiscussPosts(userId, offset, limit, orderMode);}//查询行数的方法public int findDiscussPostRows(int userId) {if (userId == 0) {return postRowsCache.get(userId);}logger.debug("load post rows from DB.");return discussPostMapper.selectDiscussPostRows(userId);}
测试类:
package com.example.demo;import com.example.demo.entity.DiscussPost;
import com.example.demo.service.DiscussPostService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;import java.util.Date;@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = DemoApplication.class)
public class CaffeineTests {@Autowiredprivate DiscussPostService postService;@Testpublic void initDataForTest() {for (int i = 0; i < 300000; i++) {DiscussPost post = new DiscussPost();post.setUserId(111);post.setTitle("互联网求职暖春计划");post.setContent("今年的就业形势,确实不容乐观");post.setCreateTime(new Date());post.setScore(Math.random() * 2000);postService.addDiscussPost(post);}}@Testpublic void testCache() {System.out.println(postService.findDiscussPosts(0, 0, 10, 1));System.out.println(postService.findDiscussPosts(0, 0, 10, 1));System.out.println(postService.findDiscussPosts(0, 0, 10, 1));System.out.println(postService.findDiscussPosts(0, 0, 10, 0));}}
2.2 压力测试
使用 jmeter 工具测试:
未添加缓存:
添加缓存:
相关文章:

18.将文件上传至云服务器 + 优化网站的性能
目录 1.将文件上传至云服务器 1.1 处理上传头像逻辑 1.1.1 客户端上传 1.1.2 服务器直传 2.优化网站的性能 2.1 本地缓存优化查询方法 2.2 压力测试 1.将文件上传至云服务器 客户端上传:客户端将数据提交给云服务器,并等待其响应;用户…...
Linux: module: kheaders;CONFIG_IKHEADERS
文章目录 参考错误开一个玩笑。configcommit参考 https://github.com/iovisor/bcc/pull/2312 https://github.com/iovisor/bcc/pull/3588 https://bugs.gentoo.org/809347 https://lore.kernel.org/lkml/20190408212855.233198-1-joel@joelfernandes.org/ 错误 <built-in…...

Page 251~254 Win32 GUI项目
win32_gui 源代码: #if defined(UNICODE) && !defined(_UNICODE)#define _UNICODE #elif defined(_UNICODE) && !defined(UNICODE)#define UNICODE #endif#include <tchar.h> #include <windows.h>/* Declare Windows procedure */…...

Kafka(七)可靠性
目录 1 可靠的数据传递1.1 Kafka的可靠性保证1.2 复制1.3 Broker配置1.3.1 复制系数1.3.2 broker的位置分布1.3.3 不彻底的首领选举1.3.4 最少同步副本1.3.5 保持副本同步1.3.6 持久化到磁盘flush.messages9223372036854775807flush.ms9223372036854775807 1.2 在可靠的系统中使…...

Spring Data JPA入门到放弃
参考文档:SpringData JPA:一文带你搞懂 - 知乎 (zhihu.com) 一、 前言 1.1 概述 Java持久化技术是Java开发中的重要组成部分,它主要用于将对象数据持久化到数据库中,以及从数据库中查询和恢复对象数据。在Java持久化技术领域&a…...

MES系统数据采集的几种方式
生产制造执行MES系统具有能够帮助企业实现生产数据收集与分析、生产计划管理、生产过程监控等的功能板块,在这里小编就不一一介绍了,主要讲讲它的数据采集功能板块,可以说,数据采集是该系统进行数据统计与生产管理等后续工作的基础…...

铭文 LaunchPad 平台 Solmash 推出早鸟激励计划
为感谢用户对Solmash的支持,Solmash 特别推出“Solmash早鸟激励计划”,以回馈社区的早期参与者,这是专为已经参与Staking Pool或Honest Pool的用户推出的激励。 Solmash NFT激励 被列入早鸟计划的用户,可通过点击:sol…...
【前端规范】
1 前言 HTML 作为描述网页结构的超文本标记语言,一直有着广泛的应用。本文档的目标是使 HTML 代码风格保持一致,容易被理解和被维护。 2 代码风格 2.1 缩进与换行 [强制] 使用 4 个空格做为一个缩进层级,不允许使用 2 个空格 或 tab 字符…...

12、JVM高频面试题
1、JVM的主要组成部分有哪些 JVM主要分为下面几部分 类加载器:负责将字节码文件加载到内存中 运行时数据区:用于保存java程序运行过程中需要用到的数据和相关信息 执行引擎:字节码文件并不能直接交给底层操作系统去执行,因此需要…...

【Docker】Docker安装入门教程及基本使用
🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《Docker实战》。🎯🎯 &…...

语义解析:如何基于SQL去实现自然语言与机器智能连接的桥梁
🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 💫个人格言:"没有罗马,那就自己创造罗马~" 目录 语义解析 定义 作用 语义解析的应用场景 场景一: 场景二: 总结语…...

Java项目:117SpringBoot动漫论坛网站
博主主页:Java旅途 简介:分享计算机知识、学习路线、系统源码及教程 文末获取源码 117SpringBoot动漫论坛网站 一、项目介绍 动漫论坛网站是由SpringBootMybatis开发的,旅游网站分为前台和后台,前台为用户浏览,后台进…...

Jenkins基础篇--添加节点
节点介绍 Jenkins 拥有分布式构建(在 Jenkins 的配置中叫做节点),分布式构建能够让同一套代码在不同的环境(如:Windows 和 Linux 系统)中编译、测试等。 Jenkins 运行的主机在逻辑上是 master 节点,下图是主节点和从节点的关系。 添加节点 …...

【C++】手撕 list类(包含迭代器)
目录 1,list的介绍及使用 2,list_node 3,list_node() 3,list 4,list() 5,push_back(const T& x) 6,print() 7,_list_iterator 8,operator*() 9,…...
@Autowired 和 @Resource 的区别是什么?
Java面试题目录 Autowired 和 Resource 的区别是什么? Autowired 是 Spring 提供的注解。默认的注入方式为byType(根据类型进行匹配)。 Resource 是 JDK 提供的注解。默认注入方式为 byName(根据名称进行匹配)。 当一…...
栈和排序.
给你一个1->n的排列和一个栈,入栈顺序给定 你要在不打乱入栈顺序的情况下,对数组进行从大到小排序 当无法完全排序时,请输出字典序最大的出栈序列 输入 第一行一个数n 第二行n个数,表示入栈的顺序,用空格隔开&…...
springboot 多数据源怎么配置在控制台的sql打印日志
程序员的公众号:源1024,获取更多资料,无加密无套路! 最近整理了一波电子书籍资料,包含《Effective Java中文版 第2版》《深入JAVA虚拟机》,《重构改善既有代码设计》,《MySQL高性能-第3版》&…...
【WinForms 窗体】常见的“陷阱”
当涉及到 WinForms 窗体编程时,我们可能会遇到一些常见的问题。在本篇博客中,我将为你提供一些常见问题的解决方案。 跨线程访问控件 在 WinForms 中,当在非UI线程上执行操作并尝试访问 UI 控件时,会引发跨线程访问异常。为了解决…...

Android readelf 工具查找函数符号
ELF(Executable and Linkable Format)是一种执行文件和可链接文件的格式。它是一种通用的二进制文件格式,用于在各种操作系统中存储可执行程序、共享库和内核模块。 Android 开发当中的 so 库本质上就是一种特殊类型的 ELF 文件,…...

MySQL-索引回顾
索引是面试高频问答题,参考百度/CSDN/尚硅谷/黑马程序员/阿里云开发者社区,决定将索引知识回顾一下,忘记时,点开即可,时刻保持更新,事不宜迟,即刻享用。 索引概述 索引(index&#…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...