计算机视觉项目中的文件批量操作与文件批量预处理
计算机视觉项目中的文件批量操作与文件批量预处理
目录
- 数据集制作
- 文件批量重命名
- 文件批量移动
- 将文件批量按照一定格式进行重命名
- 修改xml文件内容的方法
引言
在计算机视觉项目中,文件批量操作和文件批量预处理是必不可少的步骤。它们涉及处理大量的图像文件,包括读取、处理、保存和预处理。本文将介绍一些常见的技术和方法,以帮助您在计算机视觉项目中有效地进行文件批量操作和文件批量预处理。
1. 数据集制作
人工智能就是有多少人工产生多少智能,在计算机视觉项目中,数据集至关重要,下面是如何使用视频抽帧技术实现数据集的制作。以下代码中_videoPlay函数是实时显示导入的视频,CutVideo函数实现手动抽帧,在显示时按c键则抽取当前帧,按Esc键关闭视频。ExtractAll函数是自动抽帧功能,frameGap参数是隔多少帧进行自动抽取。
class ExtractImg(object):def __init__(self, videopath: Path, savepath: Path, delay: int = 1) -> None:self.spath = savepathself.vpath = videopathself.delay = delaycv2.namedWindow("cv", cv2.WINDOW_NORMAL)cv2.resizeWindow("cv", 640, 480)self.cap = cv2.VideoCapture(str(self.vpath))self._timeflag = 0if not savepath.exists():os.mkdir(Path(savepath))def _videoPlay(self, size: list) -> None:self.cap.set(3, size[0])self.cap.set(4, size[1])while self.cap.isOpened():ret, frame = self.cap.read()# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)cv2.imshow("cv", frame)if cv2.waitKey(self.delay) & 0xFF == ord('c'):cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")time.sleep(1)elif cv2.waitKey(self.delay) & 0xFF == 27:breakdef ExtractAll(self, frameGap: int = 3) -> None:"""这是将视频流中的帧全部抽出:frame: 跳帧:return:"""while self.cap.isOpened():self._timeflag += 1ret, frame = self.cap.read()if ret:cv2.imshow("cv", frame)if self._timeflag % frameGap == 0:cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")if (cv2.waitKey(self.delay) & 0xFF == 27) or not ret:breakcv2.destroyAllWindows()self.cap.release()self._timeflag = 0def CutVideo(self) -> None:"""这是手动抽帧:return:"""ifm = input("文件中已经存在{}张图片,是否有继续添加""(Y or N):".format(len(os.listdir(self.spath))))if self.spath.exists() and ifm == 'Y':self._videoPlay(size=[640, 480])elif self.spath.exists() and ifm == 'N':return Noneelse:print("\n请输入Y(yes)或者N(no)")cv2.destroyAllWindows()self.cap.release()
2. 文件批量重命名
将文件夹下的图片进行升序的重命名。
def statistics(path: Union[str, Path], dstpath: Union[Path, str], count: int = 0, random: bool = False) -> None:"""这是存放图片的文件夹安升序重命名:param path:需要重命名的文件文件:param count:观察图片总数添加使用"""assert isinstance(path, (Path, str)), print("请输入的路径")l = os.listdir(str(path))if not Path.exists(dstpath):Path.mkdir(dstpath)# l = sorted(l)print(l)# print(l)print("存在文件{}张!".format(len(l)))if random:np.random.shuffle(l)# print(l)# 将保存图片文件中的图片按照升序的方法重命名suffix = Path(l[0]).suffixfor file in tqdm(l):src = PurePath.joinpath(path, file)dst = PurePath.joinpath(dstpath, Path(str(count + int(Path(file).stem))).with_suffix(suffix))os.rename(src, dst)
3. 文件批量移动
下面的是将文件批量按照一定规则挑选出来放到目标文件夹下。
def choosen(src: Union[str, Path] , folder: Union[Path,str] ,dst: Union[str, Path] , suffix: str) -> None:"""1.将xml/jpg文件夹中的文件名字拿出来并且在jpg/xml对应的文件夹中将名称相同的文件挑出来2.将文件夹中的文件随取出:param xmlsrc:目标xml文件:param imgsrc:frameImg文件:param dst:根据xml挑选出的img文件:return: None"""# l = os.listdir(str(xmlsrc))if not isinstance(folder,Path):pa = Path(folder)if not isinstance(src,Path):l = Path(src)# parent = src.parentfor i in l.iterdir():file = Path(i.name).with_suffix(suffix)(pa / file).rename(Path(dst) / file)
4. 将文件批量按照一定格式进行重命名
下面将文件按照5位数字的格式进行重命名 1.jpg->00001.jpg。
def batchrenames(src: Union[str, Path], dst: Union[str, Path], sorted: bool = False) -> None:"""进行特定格式的重命名:param src:原文件:param dst: 存储文件:param sorted: 是否已经有顺序,若有学按照1.jpg ->00001.jpg:return: None"""d = {1: "0000", # 这是命名格式的字典2: "000",3: "00",4: "0",5: ""}l = os.listdir(src)suffix = Path(l[0]).suffixl.sort(key=lambda x: int(x.split('.')[0]))if sorted:for obj in tqdm(l):old = PurePath.joinpath(src, obj)new = PurePath.joinpath(dst, d[len(obj.split('.')[0])] + obj.split('.')[0] + suffix)os.rename(old, new)else:# for c, i in tqdm(enumerate(l)):pass
5. 修改xml文件内容的方法
这是修改xml文件内容的代码。
def revampXml(xml_path: Union[Path, str], update_content: str) -> None:"""这是一个修改xml文件内容的方法,将xml文件爱中的类别改称另一个类别:param xml_path: 存放xml文件的路径:param xml_dw: xpath关键字:param update_content: 更新的内容:return:None"""# 打开xml文档if not isinstance(xml_path, Path):xml_path = Path(xml_path)for i in tqdm(xml_path.iterdir()):xmlfile = xml_path / f"{i}"doc = ET.parse(xmlfile)root = doc.getroot()# 查找修改路劲for obj in root.iter("object"):sub1 = obj.find("name")if sub1.text == "motorboat":# 修改标签内容sub1.text = update_content# 保存修改doc.write(xmlfile)
总结
本文介绍了计算机视觉项目中的文件批量操作与文件批量预处理技术。 掌握这些技术将使您能够高效地处理大规模的图像数据,并为计算机视觉项目的成功实施提供强大的支持。
希望本文对您在计算机视觉项目中的文件批量操作与文件批量预处理有所启发!让我们一起推动计算机视觉的发展,创造更多可能性!
以下是完整代码
# -*- coding: utf-8 -*-
# @Author : cvYouTian
# @Software: PyCharmfrom pathlib import Path, PurePath
import xml.etree.ElementTree as ET
from typing import Union
import numpy as np
# import torch
from tqdm import tqdm
import time
import cv2
import osclass ExtractImg(object):def __init__(self, videopath: Path, savepath: Path, delay: int = 1) -> None:self.spath = savepathself.vpath = videopathself.delay = delaycv2.namedWindow("cv", cv2.WINDOW_NORMAL)cv2.resizeWindow("cv", 640, 480)self.cap = cv2.VideoCapture(str(self.vpath))self._timeflag = 0if not savepath.exists():os.mkdir(Path(savepath))def _videoPlay(self, size: list) -> None:self.cap.set(3, size[0])self.cap.set(4, size[1])while self.cap.isOpened():ret, frame = self.cap.read()# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)cv2.imshow("cv", frame)if cv2.waitKey(self.delay) & 0xFF == ord('c'):cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")time.sleep(1)elif cv2.waitKey(self.delay) & 0xFF == 27:breakdef ExtractAll(self, frameGap: int = 3) -> None:"""这是将视频流中的帧全部抽出:frame: 跳帧:return:"""while self.cap.isOpened():self._timeflag += 1ret, frame = self.cap.read()if ret:cv2.imshow("cv", frame)if self._timeflag % frameGap == 0:cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")if (cv2.waitKey(self.delay) & 0xFF == 27) or not ret:breakcv2.destroyAllWindows()self.cap.release()self._timeflag = 0def CutVideo(self) -> None:"""这是手动抽帧:return:"""ifm = input("文件中已经存在{}张图片,是否有继续添加""(Y or N):".format(len(os.listdir(self.spath))))if self.spath.exists() and ifm == 'Y':self._videoPlay(size=[640, 480])elif self.spath.exists() and ifm == 'N':return Noneelse:print("\n请输入Y(yes)或者N(no)")cv2.destroyAllWindows()self.cap.release()@staticmethoddef statistics(path: Union[str, Path], dstpath: Union[Path, str], count: int = 5305, random: bool = False) -> None:"""这是存放图片的文件夹安升序重命名:param path:需要重命名的文件文件:param count:观察图片总数添加使用"""assert isinstance(path, (Path, str)), print("请输入的路径")l = os.listdir(str(path))if not Path.exists(dstpath):Path.mkdir(dstpath)# l = sorted(l)print(l)# print(l)print("存在文件{}张!".format(len(l)))if random:np.random.shuffle(l)# print(l)# 将保存图片文件中的图片按照升序的方法重命名suffix = Path(l[0]).suffixfor file in tqdm(l):src = PurePath.joinpath(path, file)dst = PurePath.joinpath(dstpath, Path(str(count + int(Path(file).stem))).with_suffix(suffix))os.rename(src, dst)@staticmethoddef choosen(src: Union[str, Path]="/home/you/Desktop/2023海上高速目标检测/val", folder: Union[Path,str]="/home/you/Desktop/2023海上高速目标检测/annotations",dst: Union[str, Path]="/home/you/Desktop/2023海上高速目标检测/train", suffix: str=".xml") -> None:"""1.将xml/jpg文件夹中的文件名字拿出来并且在jpg/xml对应的文件夹中将名称相同的文件挑出来2.将文件夹中的文件随取出:param xmlsrc:目标xml文件:param imgsrc:frameImg文件:param dst:根据xml挑选出的img文件:return: None"""# l = os.listdir(str(xmlsrc))if not isinstance(folder,Path):pa = Path(folder)if not isinstance(src,Path):l = Path(src)# parent = src.parentfor i in l.iterdir():file = Path(i.name).with_suffix(suffix)(pa / file).rename(Path(dst) / file)@staticmethoddef batchrenames(src: Union[str, Path], dst: Union[str, Path], sorted: bool = False) -> None:"""进行特定格式的重命名:param src:原文件:param dst: 存储文件:param sorted: 是否已经有顺序,若有学按照1.jpg ->00001.jpg:return: None"""d = {1: "0000", # 这是命名格式的字典2: "000",3: "00",4: "0",5: ""}l = os.listdir(src)suffix = Path(l[0]).suffixl.sort(key=lambda x: int(x.split('.')[0]))if sorted:for obj in tqdm(l):old = PurePath.joinpath(src, obj)new = PurePath.joinpath(dst, d[len(obj.split('.')[0])] + obj.split('.')[0] + suffix)os.rename(old, new)else:# for c, i in tqdm(enumerate(l)):pass@staticmethoddef text(file: Union[Path, str]):l = []f = open(file)for i in f.readlines():i = i.strip()stem = Path(i).stemsuffix = Path(i).suffixn1, n2 = int(stem) - 1, int(stem) + 1l.append(str(n1) + ".xml")l.append(str(n2) + ".xml")print(l)@staticmethoddef revampXml(xml_path: Union[Path, str], update_content: str) -> None:"""这是一个修改xml文件内容的方法,将xml文件爱中的类别改称另一个类别:param xml_path: 存放xml文件的路径:param xml_dw: xpath关键字:param update_content: 更新的内容:return:None"""# 打开xml文档if not isinstance(xml_path, Path):xml_path = Path(xml_path)for i in tqdm(xml_path.iterdir()):xmlfile = xml_path / f"{i}"doc = ET.parse(xmlfile)root = doc.getroot()# 查找修改路劲for obj in root.iter("object"):sub1 = obj.find("name")if sub1.text == "motorboat":# 修改标签内容sub1.text = update_content# 保存修改doc.write(xmlfile)@staticmethoddef movefile(folder_path: Union[Path, str], dst: Union[Path, str], suffix: str) -> None:"""批量移动剪切文件:param folder_path: 原文件夹路径:param dst: 目标文件夹路径:param suffix: 移动的文件格式/后缀:return:"""if not isinstance(folder_path, Path):folder_path = Path(folder_path)# for i in folder_path.iterdir():# if i.is_dir():# ExtractImg.movefile(folder_path / i, suffix, res)# else:# if i.suffix == suffix:# res.append(str(i))# # return res if suffix is None or suffix == "" else list(filter(lambda x: str(x).endswith(suffix),res))# return resfor i in tqdm(folder_path.rglob(f"*{suffix}")):i.rename(dst / i.name)@staticmethoddef convert_box(size, box):dw, dh = 1. / size[0], 1. / size[1]x, y, w, h = (box[0] + box[1]) / 2.0 - 1, (box[2] + box[3]) / 2.0 - 1, box[1] - box[0], box[3] - box[2]return x * dw, y * dh, w * dw, h * dhif __name__ == "__main__":# 目标视频文件videopath = Path("videoSet/seabird6.mp4")# 图片保存文件savepath = Path("./dataset/imgs")# xin = Path("./VOC6detect/imgss")# savepath = Path("frameSave")# 目标xml文件# xmlpath = Path("./VOC6detect/annotations")# old = Path("/home/you/Desktop/dateset/20(pass)/seabird5")# new = Path("/home/you/Desktop/dateset/11(pass)/temp")# pa = Path("./labels/")# xin = Path()# renamepath = Path("/home/you/Desktop/dateset/4(pass)/a-1")# 实例化a = ExtractImg(videopath=videopath, savepath=savepath)a.choosen()# VOC2YOLO# a.convert_label()# 将帧全部抽出# a.ExtractAll(frameGap=8)# 手动抽帧# a.CutVideo()# 根据xml文选出对应的文件# a.choosen(xmlsrc=xmlpath, imgsrc=savepath, dst=xin)# 将数字命名的图片按照加上一个数字的方式命名# a.statistics(path=Path("./DATA/xml"), dstpath=Path("./DATA/t"), count=5305)# 对已经有顺序或者没顺序的文件进行特定格式的重命名78.jpg -> 00078.jpg# a.batchrenames(src=new, dst=old, sorted=True)# a.text("./data1.txt")# 对xml文件进行修改# a.revampXml(xml_path= "/home/you/Desktop/tools/dataset/annotations", update_content="speedboat")# 批量拿到文件夹中的某格式的文件# a.movefile(folder_path="/home/you/Desktop/网上快艇", dst=pa, suffix=".jpg")相关文章:
计算机视觉项目中的文件批量操作与文件批量预处理
计算机视觉项目中的文件批量操作与文件批量预处理 目录 数据集制作文件批量重命名文件批量移动将文件批量按照一定格式进行重命名修改xml文件内容的方法 引言 在计算机视觉项目中,文件批量操作和文件批量预处理是必不可少的步骤。它们涉及处理大量的图像文件&am…...
PHP数组转对象和对象转数组
PHP数组转对象和对象转数组 <?php function array_to_object($arr){$obj new stdClass();foreach ($arr as $key > $val) {if (is_array($val) || is_object($val)) {$obj->$key array_to_object($val);} else {$obj->$key $val;}}return $obj; } function o…...
前后端分离开发中的传参
1.post请求,后台代码使用RequestBody注解接收前端传过来的参数 PostMapping("/saveHosp") public Result SaveHosp(RequestBody HospitalSet hospitalSet){//此处省略中间代码......} 此时前端传过来的参数须为JSON格式,前端VUE传参数为&…...
mount: wrong fs type, bad option, bad superblock报错 ubuntu
问题描述 mount: wrong fs type, bad option, bad superblock Ubuntu无法挂载磁盘。 原因 很大概率是你的硬盘是NTFS。 解决 sudo apt install ntfs-3g即可。...
【图像分类】CNN+Transformer结合系列.3
介绍两篇图像分类的论文:ResMLP(arXiv2305),MetaFormer(CVPR2022),两者都与Transformer有关系,前者基于transformer结构的特点设计ResMLP,后者认为宏观架构才是Transform…...
IDA分析实例android_crackme/EasyJNI/Transformers/pingan2
文章目录 第一个实例android_crackme将32位的android_server放到手机目录下给android_server赋予root更改root用户组运行android_serverpc端端口转发安装apk,并运行app打开32位IDA并attach到进程先使用jadx看java层逻辑定位要分析的方法IDA 给两个方法打断点 第二个…...
拿捏--->求一元二次方程的根
文章目录 题目描述算法思路代码示例 题目描述 从键盘输入a, b, c的值,编程计算并输出一元二次方程ax2 bx c 0的根,当a 0时,输出“Not quadratic equation”,当a ≠ 0时,根据△ b2 - 4ac的三种情况计算并输出方程…...
深入浅出之Docker Compose详解
目录 1.Docker Compose概述 1.1 Docker Compose 定义 1.2 Docker Compose产生背景 1.3 Docker Compose 核心概念 1.4 Docker Compose 使用步骤 1.5 Docker Compose 常用命令 2. Docker Compose 实战 2.1 Docker Compose下载和卸载 2.2 Docker Compose 项目概述 2.3 Do…...
spring5源码篇(12)——spring-mvc请求流程
spring-framework 版本:v5.3.19 文章目录 一、请求流程1、处理器映射器1.1、 RequestMappingHandlerMapping1.2、获取对应的映射方法1.3、添加拦截器 2、获取合适的处理器适配器3、通过处理器适配器执行处理器方法3.1、拦截器的前置后置3.2、处理器的执行3.2.1 参数…...
风辞远的科技茶屋:来自未来的信号枪
很久之前,有位朋友问我,现在科技资讯这么发达了,你们还写啊写做什么呢? 我是这么看的。最终能够凝结为资讯的那个新闻点,其实是一系列事情最终得出的结果,而这个结果又会带来更多新的结果。其中这些“得出”…...
MongoDB教程-8
ObjectId 在之前的所有章节中,我们一直在使用MongoDB的Object Id。在本章中,我们将了解ObjectId的结构。 ObjectId是一个12字节的BSON类型,具有以下结构-- 1. 前4个字节代表自unix epoch以来的秒数 接下来的3个字节是机器标识符 接下来的2…...
Redis 理论部分
前面写了很多redis项目,今天在通过redis的理论加深redis的了解,顺便做个总结 Redis 理论部分 1.redis 速度快的原因 纯内存操作单线程操作,避免频繁的上下文切换以及资源争用的问题,多线程需要占用更多的cpu资源采用非阻塞I/O多…...
Android—Monkey用法
文章目录 Monkey知识 Monkey知识 介绍 Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中。它向系统发送伪随机的用户事件流(如按键输入、触摸屏输入、手势输入等),实现对正在开发的应用程序进行压力测试。Monkey测试是一种为了测试软…...
几个影响 cpu cache 性能因素及 cache 测试工具介绍
》内核新视界文章汇总《 文章目录 1 cache 性能及影响因素1.1 内存访问和性能比较1.2 cache line 对性能的影响1.3 L1 和 L2 缓存大小1.4 指令集并行性对 cache 性能的影响1.5 缓存关联性对 cache 的影响1.6 错误的 cacheline 共享 (缓存一致性)1.7 硬件设计 2 cpu cache benc…...
Java从入门到精通(二)· 基本语法
Java从入门到精通(二) 基本语法 一 变量 1.字面量 计算机是用来处理数据的,字面量就是告诉程序员:数据在程序中的书写格式。 特殊的字符: \n 表示换行, \t 表示一个制表符,即一个tab 2.变量…...
云安全攻防(三)之 面向云原生环境的安全体系
面向云原生环境的安全体系 根据云原生环境的构成,面向云原生环境的安全体系可包含三个层面的安全体制,它们分别是容器安全、编排系统安全和云原生应用安全,下面,我们逐步来讲解这三点: 容器安全 容器环境࿰…...
BGP汇总和破解水平分割
一,BGP的宣告问题 在BGP协议中每台运行BGP的设备上,宣告本地直连路由在BGP协议中运行BGP协议的设备来宣告,通过IGP学习到的,未运行BGP协议设备产生的路由; 在BGP协议中宣告本地路由表中路由条目时,将携带本…...
BUG:pm2启动verdaccio报错:Invalid or unexpected toke
输入命令: pm2 state verdaccio 问题描述: pm2 logs verdaccio报错翻译:数据格式错误 导致我呢提原因,没有找到运行文件, 发现问题:因为命令默认查找verdaccio是去系统盘查找。 解决方式 1:…...
Zookeeper笔记
为什么要使用Zookeeper dubbo需要一个注册中心,而Zookeeper是我们在使用Dubbo是官方推荐的注册中心 Zookeeper介绍 Zookeeper的集群机制 Zookeeper是为了其他分布式程序提供服务的,所以不能随便就挂了。Zookeeper的集群机制采取的是半数存活机制。也…...
【视觉SLAM入门】5.1. 特征提取和匹配--FAST,ORB(关键点描述子),2D-2D对极几何,本质矩阵,单应矩阵,三角测量,三角化矛盾
"不言而善应" 0. 基础知识1. 特征提取和匹配1.1 FAST关键点1.2 ORB的关键点--改进FAST1.3 ORB的描述子--BRIEF1.4 总结 2. 对极几何,对极约束2.1 本质矩阵(对极约束)2.1.1 求解本质矩阵2.1.2 恢复相机运动 R , t R,t R,…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
