首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

“消息驱动、事件驱动、流 ”基础概念解析

作者:肯梦 阿里云消息队列 RocketMQ 5.0 实现了全新升级,实现了从“消息”到“消息、事件、流”的大融合,基于此,Message-Driven、Event-Driven、Streaming 这三个词是近期消息领域高频词,但由于概念过于新,很多同学其实是不太理解这里的异同。本文把三个概念重新整理下,梳理出比较明确的概念讲给大家。 背景 首先这三个概念具体翻译如下: Message-Driven: 消息驱动的通信; Event- Driven: 事件驱动的通信; Streaming: 流模式。 这三个模式都是类似异步通信的模式,发送消息的服务不会等待消费消息服务响应任何数据,做服务解耦是三个模式共同的特性; 只要是在服务通讯领域内,在选型时还要考虑如下特性: 排序: 是否可以保证特定的顺序交付; 事务: 生产者或消费者是否可以参与分布式事务; 持久化: 数据如何被持久化,以及是否可以重放数据; 订阅过滤: 是否拥有根据Tag或其他字段做订阅过滤的能力; At – least -once(最少交付一次),At-most-once(最多交付一次),Exactly-once (精确交付)。 通用背景介绍完,依次来看看各个模型代表的是什么意思。 消息驱动 Message-Driven 在消息驱动通信中,一般链路就是消息生产者(Producer)向消息消费者(Consumer)发送消息。模型如下: 消息驱动模式下通常会用到中间件,比较常见的中间组件有 RocketMQ,Kafka,RabbitMQ 等。这些中间件的目的是缓存生产者投递的消息直到消费者准备接收这些消息,以此将两端系统解耦。 在消息驱动架构中,消息的格式是基于消费者的需求制定的;消息传递可以是一对一,多对多,一对多或多对一。 消息驱动通讯比较常见的一个例子是商品订单推送,上游组件负责生成订单,下游组件负责接收订单并处理。通过这样的通讯方式上游生成组件其实无需关心整个订单的生命周期,更专注于如何快速生成订单,使单个组件的性能得以提升。! 消息驱动模式在服务之间提供了轻的耦合(这部分耦合指代 Producer/Consumer SDK),并可以对生产和消费服务根据诉求进行扩展。 事件驱动Event-Driven 首先要申明一个观点:事件驱动其实是对消息驱动方法的改进,它对消息体大小,消息格式做了较为严格的限制,这层基于消息的限制封装其实就称为事件(Event)。 在事件驱动模式中,生产者发布事件来表示系统变更,任何感兴趣且有权限接入的服务都可以订阅这些事件,并将这些事件作为触发器来启动某些逻辑/存储/任务。​ 事件驱动的模式可以是一对一,多对一,一对多或多对多。通常情况下一般是多个目标根据过滤条件执行不同的事件。 在事件驱动架构中,事件的格式是由生产者根据事件标准协议制定的;由于更规范限制和封装,事件的生产者完全不需要关心有哪些系统正在消费它生成的事件。 事件不是命令,事件不会告诉消费者如何处理信息,他们的作用只是告诉消费者此时此刻有个事件发生了;事件是一份不可变的数据,重要的数据,它与消息的数据价值相同;通常情况下当某个事件发生并执行时,往往伴随着另一个事件的产生。 事件驱动提供了服务间的最小耦合,并允许生产服务和消费服务根据需求进行扩展;事件驱动可以在不影响现有服务的情况下添加各类新增组件。 事件驱动也可以举一个非常贴切的例子,我们以“客户购买完一款商品”为一个事件,举证在事件场景的应用: CRM(客户关系系统)系统接收到客户购买信息,可自行更新客户的购买记录; EMR(库存管理系统) 系统接收到客户购买信息,动态调整库存并及时补货; 快递服务接收到客户购买信息,自行打单并通知快递公司派送。 这么看,事件驱动模式是不是可以应用并出现在任何地方! 在EventBridge产品化方向,也正是由于针对消息做了一些标准化封装,才有可能实现譬如针对事件本身的 filter(过滤) ,transform(转换),schema(事件结构),search(查询) 等能力。这些能力也拓展出更多针对事件驱动特有的场景功能及相关特性。 流Streaming 流是一组有序的无界事件或数据,执行操作通常是固定的某个事件段(e.g. 00:00 – 12:00)或一个相对事件(E.g. 过去 12 小时)。 通常情况下单个事件往往就是使用事件本身,但是对于流可能的操作大概率是过滤,组合,拆分,映射等等。 流的操作可以是无状态也可以是有状态的: 对于单个事件操作是无状态的,包括过滤和映射; 依赖消息在流的时间或位置(e.g. offset,time)是有状态的。有状态操作中,流处理逻辑必须保留一些已被消费消息的内存。有状态包括对数据做 Batch Size,Batch Window 等。 流这里也可以举一个比较简单的例子,比如我们的物流系统在物品通过一个物流节点时会生成一个事件,但是要查到这个物品完整的流转状态事件,则必须是各个物流节点单个事件的聚合,那这个聚合事件就是流事件。 Kafka 是最典型的流式中间件,在流式场景中,事件的位置信息至关重要。通常情况下位置信息(E.g. offset)是由消费者托管的。 事件规范标准 聊完 Event 和 Streaming 是什么,再来补充一点有关于它们的规范。 事件规范存在的目的是为了清晰事件生产者和消费者的关系,目前主要有两部分:AsyncAPI 和 CloudEvents; AsyncAPI: 基于事件 API 提供了与之对应的 Open API 和 Swagger 等;CloudEvents: 侧重于处理事件的元数据。 下面也重点介绍一些关于 CloudEvents 的相关概念参考:CloudEvents 的核心其实是定义了一组关于不同组件间传输事件的元数据,以及这些元数据应该如何出现在消息体中。 其主旨大抵如下: 事件规范化; 降低平台集成难度; 提高 FaaS 的可移植性; 源事件可追踪; 提升事件关联性 准确的事件体,事件信息才可以做出更稳定的系统架构,永远保持对事件的敬畏。 附 一些术语及定义: Occurrence: 发生,指事件逻辑上的发生,基于某种情况,事件出现了; Event: 事件,表示事件以及上下文的数据记录。可以根据事件中的信息决定路由,但事件本身并不包含路由信息; Producer: 生产者,真正创造事件的实例或组件; Source: 源,事件发生的上下文,可以由多个 producer 组成; Consumer: 消费者,接收事件并对事件进行消费; Intermediary:中介,接收包含事件的消息(message),并转发给下一个接收方,类似路由器; Context: 上下文,上下文元数据被封装到 context attributes 中,用来判断事件与其它系统的关系; Data: 数据,也可以叫做 payload; EventFormat:事件格式,例如 json; Message: 消息,封装事件并将其从 source 传递到 destination; Protocol: 协议,可以是行业标准如 http,开源协议如 Kafka 或者供应商协议如 AWS Kinesis; Protocol Binding: 协议绑定,描述如何通过给定的协议收发事件,如何将事件放到消息里。 重磅推荐 本文旨在帮助大家对近期消息领域的高频词“消息驱动(Message-Driven),事件驱动(Event-Driven)和流(Streaming)”有更清晰的了解和认知,其中事件驱动 EDA 作为 Gartner 预测的十大技术趋势之一, EventBridge 作为下一代消息中间件,也是目前的重点方向之一。如果大家感兴趣,想要有更多了解,可关注**「阿里云 EventBridge 系列公开课」**,完整课程正火热开讲中! 点击​​此处​​,可以直接观看课程视频~

优秀的个人博客,低调大师

Jenkins CI持续集成项目基础配置

1. 安装Jenkins sudo wget -O /etc/yum.repos.d/jenkins.repo \ https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key sudo yum upgrade sudo yum install epel-release java-11-openjdk-devel sudo yum install jenkins sudo systemctl daemon-reload 引用自:https://www.jenkins.io/doc/book/installing/linux/#red-hat-centos 2. 打开http://ip:8080 按提示操作,并安装推荐插件 3. Jenkins安装插件 ① Git 插件 ② Docker 插件 ③ Kubernetes ④ Kubernetes Cli ⑤ Config File Provider ⑥ Pipeline Utility Steps 4. Jenkins服务器安装 maven git docker jdk(安装jenkins时已经启动) 5. 配置Credentials添加证书从git上拉代码 Jenkins服务器生成密钥: ssh-keygen -t rsa -C "ci@huanke.com" 使用阿里云的codeup的话,先创建个拉取的ram账户(例如:ci),在codeup中将ram账户ci给授权为开发权限。 然后阿里云登陆ci账户,上传服务器的id_rsa.pub 到codeup仓库的ci个人设置下的ssh公钥,并设置为只读权限。 最后Jenkins设置git拉去代码凭据

优秀的个人博客,低调大师

Linux云计算-03_必备基础命令

Linux系统启动默认为字符界面,一般不会启动图形界面,所以对命令行的熟练程度能更加方便、高效的管理Linux系统。 本章介绍Linux系统必备命令各项参数及功能场景,Linux常见命令包括:cd、ls、pwd、mkdir、rm、cp、mv、touch、cat、head、tail、chmod、vim等。 ### 1 cd命令详解 cd命令主要用于目录切换,例如:cd /home切换至/home目录,cd /root表示切换至/root目录 ;cd ../切换至上一级目录;cd ./切换至当前目录。 其中.和..可以理解为相对路径,例如cd ./test表示以当前目录为参考,表示相对于当前,而cd /home/test表示完整的路径,理解为绝对路径),如下所示: ``` [root@superman-vm01 ~]# [root@superman-vm01 ~]# cd /tmp [root@superman-vm01 tmp]# [root@superman-vm01 tmp]# cd /home [root@superman-vm01 home]# [root@superman-vm01 home]# cd .. [root@superman-vm01 /]# [root@superman-vm01 /]# cd ./home [root@superman-vm01 home]# [root@superman-vm01 home]# cd /etc/rc.d/rc3.d [root@superman-vm01 rc3.d]# [root@superman-vm01 rc3.d]# cd ../.. [root@superman-vm01 etc]# ``` ### 2 ls命令详解 ls命令主要用于浏览目录下的文件或者文件夹,使用方法参考:ls ./ 查看当前目录所有的文件和目录,ls -a 查看所有的文件,包括隐藏文件,以.开头的文件,常用参数详解如下: ``` -a, --all 不隐藏任何以.开始的项目; -A, --almost-all 列出除.及..以外的任何项目; --author 与-l同时使用时列出每个文件的作者; -b, --escape 以八进制溢出序列表示不可打印的字符; --block-size=大小 块以指定大小的字节为单位; -B, --ignore-backups 不列出任何以"~"字符结束的项目; -d, --directory 当遇到目录时列出目录本身而非目录内的文件; -D, --dired 产生适合Emacs的dired模式使用的结果; -f 不进行排序,-aU选项生效,-lst选项失效; -i, --inode 显示每个文件的inode号; -I, --ignore=PATTERN 不显示任何符合指定shell PATTERN的项目; -k 即--block-size=1K; -l 使用较长格式列出信息; -n, --numeric-uid-gid 类似-l,但列出UID及GID号; -N, --literal 输出未经处理的项目名称 (如不特别处理控制字符) ; -r, --reverse 排序时保留顺序; -R, --recursive 递归显示子目录; -s, --size 以块数形式显示每个文件分配的尺寸; -S 根据文件大小排序; -t 根据修改时间排序; -u 同-lt 一起使用:按照访问时间排序并显示; 同-l一起使用:显示访问时间并按文件名排序; 其他:按照访问时间排序; -U 不进行排序;按照目录顺序列出项目; -v 在文本中进行数字(版本)的自然排序。 ``` #### 2.1 长格式显示 -l 参数主要是可以看到文件的更详细的信息。 ``` [root@superman-vm01 ~]# ls -l / total 24 lrwxrwxrwx. 1 root root 7 Jul 4 08:08 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 Jul 4 08:22 boot drwxr-xr-x 20 root root 3320 Jul 7 06:55 dev drwxr-xr-x. 138 root root 8192 Jul 7 06:55 etc drwxr-xr-x. 3 root root 22 Jul 4 08:17 home lrwxrwxrwx. 1 root root 7 Jul 4 08:08 lib -> usr/lib lrwxrwxrwx. 1 root root 9 Jul 4 08:08 lib64 -> usr/lib64 drwxr-xr-x. 2 root root 6 Apr 11 2018 media drwxr-xr-x. 2 root root 6 Apr 11 2018 mnt drwxr-xr-x. 3 root root 16 Jul 4 08:13 opt dr-xr-xr-x 187 root root 0 Jul 7 06:55 proc dr-xr-x---. 15 root root 4096 Jul 6 08:42 root drwxr-xr-x 39 root root 1220 Jul 7 06:55 run lrwxrwxrwx. 1 root root 8 Jul 4 08:08 sbin -> usr/sbin drwxr-xr-x. 2 root root 6 Apr 11 2018 srv dr-xr-xr-x 13 root root 0 Jul 7 06:55 sys drwxrwxrwt. 27 root root 4096 Jul 7 07:01 tmp drwxr-xr-x. 13 root root 155 Jul 4 08:08 usr drwxr-xr-x. 20 root root 282 Jul 4 08:22 var [root@superman-vm01 ~]# -rw-r--r-- 第一个横行表示文件类型,常见有 - 普通文件: d 目录 l 链接文件 c 字符设备(即串行端口的接口设备,例如伪终端等) b 块设备(磁盘) s 套接字文件(通常用在网络数据连接) p 管道文件 # Linux目录结构: / 根目录 /boot 放置linux系统启动时的内核文件以及引导文件。 /dev 存放linux系统下的设备文件 /etc 系统配置文件存放的目录 /home 系统默认的用户家目录 /lib,lib64 动态连接共享库 /media 挂载可移动的设备 /opt 存放第三方软件 /root 管理员家目录 /bin,/sbin 可执行二进制命令 /run 临时文件系统目录 /srv 早期存放数据目录 /tmp 存放临时文件 /usr 应用程序目录 /var 存放动态文件,比如日志 /mnt 临时挂载目录 ``` #### 2.2 显示隐藏文件 -a参数可以显示隐藏文件。 ```shell [root@superman-vm01 ~]# ls -a . anaconda-ks.cfg .bash_logout .bashrc .config .dbus Documents .esd_auth initial-setup-ks.cfg Music Public .tcshrc Videos .. .bash_history .bash_profile .cache .cshrc Desktop Downloads .ICEauthority .local Pictures .ssh Templates .viminfo [root@superman-vm01 ~]# ``` ### 3 pwd命令详解 pwd命令主要用于显示或者查看当前所在的目录路径,如下所示: ``` [root@superman-vm01 ~]# cd /etc [root@superman-vm01 etc]# [root@superman-vm01 etc]# pwd /etc [root@superman-vm01 etc]# [root@superman-vm01 etc]# cd /etc/sysconfig/network-scripts/ [root@superman-vm01 network-scripts]# [root@superman-vm01 network-scripts]# pwd /etc/sysconfig/network-scripts [root@superman-vm01 network-scripts]# [root@superman-vm01 network-scripts]# cd /var/log [root@superman-vm01 log]# [root@superman-vm01 log]# pwd /var/log [root@superman-vm01 log]# ``` ### 4 mkdir命令详解 mkdir命令主要用于创建目录,用法mkdir dirname,命令后接目录的名称,常用参数详解如下: ``` 用法:mkdir [选项]... 目录;若指定目录不存在则创建目录; 长选项必须使用的参数对于短选项时也是必需使用的; -m, --mode=模式 设置权限模式(类似chmod),而不是rwxrwxrwx减umask; -p, --parents 需要时创建目标目录的上层目录,但即使这些目录已存在也不当作错误处理; -v, --verbose 每次创建新目录都显示信息; -Z, --context=CTX 将每个创的目录的SELinux 安全环境设置为CTX; --help 显示此帮助信息并退出; --version 显示版本信息并退出。 ``` #### 4.1 递归创建目录 -p 自动创建上级目录,如果上级目录不存在;如果目录已经,则不创建,不会提示报错。 ```shell [root@superman-vm01 ~]# mkdir /home/a/b mkdir: cannot create directory ‘/home/a/b’: No such file or directory [root@superman-vm01 ~]# [root@superman-vm01 ~]# mkdir -p /home/a/b [root@superman-vm01 ~]# ``` #### 4.2 指定目录权限 -m 可以指定创建目录时的权限。 ```shell [root@superman-vm01 ~]# mkdir /home/c [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls -l /home total 0 drwxr-xr-x 3 root root 15 Jul 7 07:27 a drwxr-xr-x 2 root root 6 Jul 7 07:31 c drwx------. 4 superman superman 97 Jul 7 07:26 superman [root@superman-vm01 ~]# [root@superman-vm01 ~]# mkdir -m 640 /home/d [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls -l /home total 0 drwxr-xr-x 3 root root 15 Jul 7 07:27 a drwxr-xr-x 2 root root 6 Jul 7 07:31 c drw-r----- 2 root root 6 Jul 7 07:32 d drwx------. 4 superman superman 97 Jul 7 07:26 superman [root@superman-vm01 ~]# ``` ### 5 rm命令详解 rm 命令主要用于删除文件或者目录,用法 rm –rf test.txt (-r表示递归,-f表示强制),常用参数详解如下: ``` 用法:rm [选项]... 文件...删除 (unlink) 文件。 -f, --force 强制删除。忽略不存在的文件,不提示确认; -i 在删除前需要确认; -I 在删除超过三个文件或者递归删除前要求确认。此选项比-i 提示内容更少,但同样可以阻止大多数错误发生; -r, -R, --recursive 递归删除目录及其内容; -v, --verbose 详细显示进行的步骤; --help 显示此帮助信息并退出; --version 显示版本信息并退出; 默认时,rm 不会删除目录,使用--recursive(-r 或-R)选项可删除每个给定的目录,以及其下所有的内容; 要删除第一个字符为"-"的文件 (例如"-foo"),请使用以下方法之一: rm -- -foo rm ./-foo ``` ### 6 cp命令详解 cp 命令主要用于拷贝文件,用法,cp old.txt /tmp/new.txt ,常用来备份,如果拷贝目录需要加-r参数,常用参数详解如下: ``` 用法:cp [选项]... [-T] 源文件 目标文件 或:cp [选项]... 源文件... 目录 或:cp [选项]... -t 目录 源文件... 将源文件复制至目标文件,或将多个源文件复制至目标目录。 长选项必须使用的参数对于短选项时也是必需使用的。 -a, --archive 等于-dR --preserve=all; --backup[=CONTROL 为每个已存在的目标文件创建备份; -b 类似--backup 但不接受参数; --copy-contents 在递归处理是复制特殊文件内容; -d 等于--no-dereference --preserve=links; -f, --force 如果目标文件无法打开则将其移除并重试(当 -n 选项; 存在时则不需再选此项); -i, --interactive 覆盖前询问(使前面的 -n 选项失效); -H 跟随源文件中的命令行符号链接; -l, --link 链接文件而不复制; -L, --dereference 总是跟随符号链接; -n, --no-clobber 不要覆盖已存在的文件(使前面的 -i 选项失效); -P, --no-dereference 不跟随源文件中的符号链接; -p 等于--preserve=模式,所有权,时间戳; --preserve[=属性列表 保持指定的属性(默认:模式,所有权,时间戳),如果; 可能保持附加属性:环境、链接、xattr 等; -c same as --preserve=context; --sno-preserve=属性列表 不保留指定的文件属性; --parents 复制前在目标目录创建来源文件路径中的所有目录; -R, -r, --recursive 递归复制目录及其子目录内的所有内容。 ``` #### 6.1 复制更新的文件 -u 只**复制源文件有更新**的,否则不执行。 ``` #第一次将abc.txt复制到/home目录: [root@superman-vm01 ~]# cat abc.txt 123 456 [root@superman-vm01 ~]# [root@superman-vm01 ~]# cp abc.txt /home #更新文件 [root@superman-vm01 ~]# echo "this is update" > abc.txt [root@superman-vm01 ~]# cat abc.txt this is update [root@superman-vm01 ~]# #-u参数复制: [root@superman-vm01 ~]# cp -u abc.txt /home cp: overwrite ‘/home/abc.txt’? y [root@superman-vm01 ~]# [root@superman-vm01 ~]# cat abc.txt this is update [root@superman-vm01 ~]# #不更新文件,再次复制,并没有进行替换: [root@superman-vm01 ~]# cp -u abc.txt /home [root@superman-vm01 ~]# ``` #### 6.2 复制软连接 -d|-a**复制软连接**,这里要注意创建软连接时最好为绝对路径,否则可能会出现失效的软连接。 ``` [root@superman-vm01 ~]# ln -s /root/abc.txt abc1.txt [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls -l total 12 lrwxrwxrwx 1 root root 13 Jul 7 07:59 abc1.txt -> /root/abc.txt -rw-r--r-- 1 root root 15 Jul 7 07:48 abc.txt -rw-------. 1 root root 2002 Jul 4 08:18 anaconda-ks.cfg drwxr-xr-x. 2 root root 6 Jul 4 08:30 Desktop drwxr-xr-x. 2 root root 6 Jul 4 08:30 Documents drwxr-xr-x. 2 root root 6 Jul 4 08:30 Downloads -rw-r--r--. 1 root root 2050 Jul 4 08:27 initial-setup-ks.cfg drwxr-xr-x. 2 root root 6 Jul 4 08:30 Music drwxr-xr-x. 2 root root 6 Jul 4 08:30 Pictures drwxr-xr-x. 2 root root 6 Jul 4 08:30 Public drwxr-xr-x. 2 root root 6 Jul 4 08:30 Templates drwxr-xr-x. 2 root root 6 Jul 4 08:30 Videos [root@superman-vm01 ~]# #如果直接复制,不带参数,会导致软连接失效,直接创建普通文件 [root@superman-vm01 ~]# cp abc1.txt /data [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls -l /data total 4 -rw-r--r-- 1 root root 15 Jul 7 08:00 abc1.txt [root@superman-vm01 ~]# #加上参数: [root@superman-vm01 ~]# cp -d abc1.txt /data cp: overwrite ‘/data/abc1.txt’? y [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls -l /data total 0 lrwxrwxrwx 1 root root 13 Jul 7 08:00 abc1.txt -> /root/abc.txt [root@superman-vm01 ~]# ``` #### 6.3 备份复制 -S 复制同名文件到目的目录时,**对源文件进行备份**,且自定义备份文件后缀名。 ``` #第一次复制 [root@superman-vm01 ~]# cp abc.txt /data [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls /data abc1.txt abc.txt [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls -l /data total 4 lrwxrwxrwx 1 root root 13 Jul 7 08:00 abc1.txt -> /root/abc.txt -rw-r--r-- 1 root root 15 Jul 7 08:03 abc.txt [root@superman-vm01 ~]# #第二次复制,对源文件进行备份: [root@superman-vm01 ~]# \cp -S ".`date +%F`" abc.txt /data [root@superman-vm01 ~]# [root@superman-vm01 ~]# ls /data abc1.txt abc.txt abc.txt.2021-07-07 [root@superman-vm01 ~]# [root@superman-vm01 ~]# ll /data total 8 lrwxrwxrwx 1 root root 13 Jul 7 08:00 abc1.txt -> /root/abc.txt -rw-r--r-- 1 root root 15 Jul 7 08:05 abc.txt -rw-r--r-- 1 root root 15 Jul 7 08:03 abc.txt.2021-07-07 [root@superman-vm01 ~]# ``` #### 6.4 通用参数 -a 如果参数都记不住,就记住它吧,可以实现递归,复制软连接,保留文件属性。 ### 7 mv命令详解 mv 命令主要用于重命名或者移动文件或者目录,用法, mv old.txt new.txt,常用参数详解如下: ``` 用法:mv [选项]... [-T] 源文件 目标文件; 或:mv [选项]... 源文件... 目录; 或:mv [选项]... -t 目录 源文件; 将源文件重命名为目标文件,或将源文件移动至指定目录。长选项必须使用的参数对于短选项时也是必需使用的。 --backup[=CONTROL] 为每个已存在的目标文件创建备份; -b 类似--backup 但不接受参数; -f, --force 覆盖前不询问; -i, --interactive 覆盖前询问; -n, --no-clobber 不覆盖已存在文件,如果您指定了-i、-f、-n 中的多个,仅最后一个生效; --strip-trailing-slashes 去掉每个源文件参数尾部的斜线; -S, --suffix=SUFFIX 替换常用的备份文件后缀; -t, --target-directory=DIRECTORY 将所有参数指定的源文件或目录; 移动至 指定目录; -T, --no-target-directory 将目标文件视作普通文件处理; -u, --update 只在源文件文件比目标文件新,或目标文件; 不存在时才进行移动; -v, --verbose 详细显示进行的步骤; --help 显示此帮助信息并退出; --version 显示版本信息并退出。 ``` ### 8 touch命令详解 touch 命令主要用于创建普通文件,用法为touch test.txt,如果文件存在,则表示修改当前文件时间,常用参数详解如下: ``` 用法:touch [选项]... 文件... 将每个文件的访问时间和修改时间改为当前时间; 不存在的文件将会被创建为空文件,除非使用-c 或-h 选项; 如果文件名为"-"则特殊处理,更改与标准输出相关的文件的访问时间; 长选项必须使用的参数对于短选项时也是必需使用的; -a 只更改访问时间; -c, --no-create 不创建任何文件; -d, --date=字符串 使用指定字符串表示时间而非当前时间; -f (忽略); -h, --no-dereference 会影响符号链接本身,而非符号链接所指示的目的地; (当系统支持更改符号链接的所有者时,此选项才有用); -m 只更改修改时间; -r, --reference=文件 使用指定文件的时间属性而非当前时间; -t STAMP 使用[[CC]YY]MMDDhhmm[.ss] 格式的时间而非当前时间; --time=WORD 使用WORD 指定的时间:access、atime、use 都等于-a; 选项的效果,而modify、mtime 等于-m 选项的效果; --help 显示此帮助信息并退出; --version 显示版本信息并退出。 ``` ### 9 cat命令详解 cat 命令主要用于查看文件内容,用法 cat test.txt 可以查看test.txt内容,常用参数详解如下: ``` 用法:cat [选项]... [文件]... 将[文件]或标准输入组合输出到标准输出。 -A, --show-all 等于-vET; -b, --number-nonblank 对非空输出行编号; -e 等于-vE; -E, --show-ends 在每行结束处显示"$"; -n, --number 对输出的所有行编号; -s, --squeeze-blank 不输出多行空行; -t 与-vT 等价; -T, --show-tabs 将跳格字符显示为^I; -u (被忽略); -v, --show-nonprinting 使用^ 和M- 引用,除了LFD和 TAB 之外; --help 显示此帮助信息并退出; --version 显示版本信息并退出。 ``` #### 9.1 查看文件内容 ``` [root@superman-vm01 ~]# cat -n /etc/fstab 1 2 # 3 # /etc/fstab 4 # Created by anaconda on Sun Jul 4 08:07:23 2021 5 # 6 # Accessible filesystems, by reference, are maintained under '/dev/disk' 7 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info 8 # 9 /dev/mapper/vg00-lv_root / xfs defaults 0 0 10 UUID=0e73085e-969b-4e8e-8aa5-83120ce58f76 /boot xfs defaults 0 0 11 /dev/mapper/vg00-lv_swap swap swap defaults 0 0 [root@superman-vm01 ~]# ``` #### 9.2 创建文件 ``` # EOF表示结束符,可以自定义,如下: # 以覆盖得方式写文件 [root@superman-vm01 ~]# cat abc.txt this is update [root@superman-vm01 ~]# [root@superman-vm01 ~]# cat > abc.txt << EOF > this is superman > EOF [root@superman-vm01 ~]# [root@superman-vm01 ~]# cat abc.txt this is superman [root@superman-vm01 ~]# # 以追加得方式写文件 [root@superman-vm01 ~]# cat abc.txt this is superman [root@superman-vm01 ~]# [root@superman-vm01 ~]# cat >> abc.txt < I am from Zhengzhou > f [root@superman-vm01 ~]# [root@superman-vm01 ~]# cat abc.txt this is superman I am from Zhengzhou [root@superman-vm01 ~]# ``` ### 10 head命令详解 head命令主要用于查看文件内容,通常查看文件前10行,head -10 /var/log/messages可以查看该文件前10行的内容,常用参数详解如下: ``` 用法:head [选项]... [文件]... 将每个指定文件的头10 行显示到标准输出。 如果指定了多于一个文件,在每一段输出前会给出文件名作为文件头。 如果不指定文件,或者文件为"-",则从标准输入读取数据,长选项必须使用的参数对于短选项时也是必需使用的; -q, --quiet, --silent 不显示包含给定文件名的文件头; -v, --verbose 总是显示包含给定文件名的文件头; --help 显示此帮助信息并退出; --version 显示版本信息并退出; -c, --bytes=[-]K 显示每个文件的前K 字节内容,如果附加"-"参数,则除了每个文件的最后K字节数据外显示剩余全部内容; -n, --lines=[-]K 显示每个文件的前K 行内容,如果附加"-"参数,则除了每个文件的最后K 行外显示剩余全部内容。 ``` ### 11 tail命令详解 tail命令主要用于查看文件内容,通常查看末尾10行,tail –fn 100 /var/log/messages可以实时查看该文件末尾100行的内容,常用参数详解如下: ``` 用法:tail [选项]... [文件]... 显示每个指定文件的最后10 行到标准输出。 若指定了多于一个文件,程序会在每段输出的开始添加相应文件名作为头。 如果不指定文件或文件为"-" ,则从标准输入读取数据。 长选项必须使用的参数对于短选项时也是必需使用的。 -n, --lines=K 输出的总行数,默认为10行; -q, --quiet, --silent 不输出给出文件名的头; --help 显示此帮助信息并退出; --version 显示版本信息并退出; -f, --follow[={name|descriptor}] 即时输出文件变化后追加的数据; -f, --follow 等于--follow=descriptor -F 即--follow=name –retry -c, --bytes=K 输出最后K字节;另外,使用-c +K 从每个文件的第K字节输出。 ``` ### 12 chmod命令详解 chmod命令主要用于修改文件或者目录的权限,例如chmod o+w test.txt,赋予test.txt其他人w写权限,常用参数详解如下: ``` 用法:chmod [选项]... 模式[,模式]... 文件... 或:chmod [选项]... 八进制模式 文件... 或:chmod [选项]... --reference=参考文件 文件,将每个文件的模式更改为指定值。 -c, --changes 类似 --verbose,但只在有更改时才显示结果 --no-preserve-root 不特殊对待根目录(默认); --preserve-root 禁止对根目录进行递归操作; -f, --silent, --quiet 去除大部份的错误信息; -R, --recursive 以递归方式更改所有的文件及子目录; --help 显示此帮助信息并退出; --version 显示版本信息并退出; -v, --verbose 为处理的所有文件显示诊断信息; --reference=参考文件 使用指定参考文件的模式,而非自行指定权限模式。 ``` ### 13 chown命令详解 chown命令主要用于文件或者文件夹宿主及属组的修改,命令格式例如chown –R root.root /tmp/test.txt,表示修改test.txt文件的用户和组均为root,常用参数详解如下: ``` 用法:chown [选项]... [所有者][:[组]] 文件... 或:chown [选项]... --reference=参考文件 文件... 更改每个文件的所有者和/或所属组。 当使用 --referebce 参数时,将文件的所有者和所属组更改为与指定参考文件相同。 -f, --silent, --quiet 去除大部份的错误信息 --reference=参考文件 使用参考文件的所属组,而非指定值; -R, --recursive 递归处理所有的文件及子目录; -v, --verbose 为处理的所有文件显示诊断信息; -H 命令行参数是一个通到目录的符号链接,则遍历符号链接; -L 历每一个遇到的通到目录的符号链接; -P 历任何符号链接(默认); --help 显示帮助信息并退出; --version 显示版本信息并退出。 ``` ### 14 echo命令详解 echo命令主要用于打印字符或者回显,例如输入echo ok,会显示ok, echo ok > test.txt 则会把ok字符覆盖test.txt内容。>表示覆盖,原内容被覆盖,>>表示追加,原内容不变。 例如echo ok >> test.txt,表示向test.txt文件追加OK字符,不覆盖原文件里的内容,常用参数详解如下: ``` 使用-e扩展参数选项时,与如下参数一起使用,有不同含义,例如: \a 发出警告声 \b 删除前一个字符 \c 最后不加上换行符号; \f 换行但光标仍旧停留在原来的位置; \n 换行且光标移至行首; \r 光标移至行首,但不换行; \t 插入tab; \v 与\f相同; \\ 插入\字符; \033[30m 黑色字 \033[0m \033[31m 红色字 \033[0m \033[32m 绿色字 \033[0m \033[33m 黄色字 \033[0m \033[34m 蓝色字 \033[0m \033[35m 紫色字 \033[0m \033[36m 天蓝字 \033[0m \033[37m 白色字 \033[0m \033[40;37m 黑底白字 \033[0m \033[41;37m 红底白字 \033[0m \033[42;37m 绿底白字 \033[0m \033[43;37m 黄底白字 \033[0m \033[44;37m 蓝底白字 \033[0m \033[45;37m 紫底白字 \033[0m \033[46;37m 天蓝底白字 \033[0m \033[47;30m 白底黑字 \033[0m ``` echo颜色打印扩展,auto_lamp_v2.sh内容如下: ``` echo -e "\033[36mPlease Select Install Menu follow:\033[0m" echo -e "\033[32m1)Install Apache Server\033[1m" echo "2)Install MySQL Server" echo "3)Install PHP Server" echo "4)Configuration index.php and start LAMP server" echo -e "\033[31mUsage: { /bin/sh $0 1|2|3|4|help}\033[0m" ``` 执行结果如图所示: ![image-20210707003248333](https://superman-1306409616.cos.ap-beijing.myqcloud.com/imageimage-20210707003248333.png) ### 15 df命令详解 df命令常用于磁盘分区查询,常用命令df –h,查看磁盘分区信息,常用参数详解如下: ``` 用法:df [选项]... [文件]... 显示每个文件所在的文件系统的信息,默认是显示所有文件系统。 长选项必须使用的参数对于短选项时也是必需使用的。 -a, --all 显示所有文件系统的使用情况,包括虚拟文件系统; -B, --block-size=SIZE 使用字节大小块; -h, --human-readable 以人们可读的形式显示大小; -H, --si 同-h,但是强制使用1000而不是1024; -i, --inodes 显示inode 信息而非块使用量; -k 即--block-size=1K; -l, --local 只显示本机的文件系统; --no-sync 取得使用量数据前不进行同步动作(默认); -P, --portability 使用POSIX 兼容的输出格式; --sync 取得使用量数据前先进行同步动作; -t, --type=类型 只显示指定文件系统为指定类型的信息; -T, --print-type 显示文件系统类型; -x, --exclude-type=类型 只显示文件系统不是指定类型信息; --help 显示帮助信息并退出; --version 显示版本信息并退出。 ``` ### 16 du命令详解 du命令常用于查看文件在磁盘中的使用量,常用命令du -sh,查看当前目录所有文件及文件及的大小,常用参数详解如下: ``` 用法:du [选项]... [文件]... 或:du [选项]... --files0-from=F 计算每个文件的磁盘用量,目录则取总用量。 长选项必须使用的参数对于短选项时也是必需使用的。 -a, --all 输出所有文件的磁盘用量,不仅仅是目录; --apparent-size 显示表面用量,而并非是磁盘用量;虽然表面用量通常会小一些,但有时它会因为稀疏文件间的"洞"、内部碎片、非直接引用的块等原因而变大; -B, --block-size=大小 使用指定字节数的块; -b, --bytes 等于--apparent-size --block-size=1; -c, --total 显示总计信息; -H 等于--dereference-args (-D); -h, --human-readable 以可读性较好的方式显示尺寸(例如:1K 234M 2G); --si 类似-h,但在计算时使用1000 为基底而非1024; -k 等于--block-size=1K; -l, --count-links 如果是硬连接,就多次计算其尺寸; -m 等于--block-size=1M; -L, --dereference 找出任何符号链接指示的真正目的地; -P, --no-dereference 不跟随任何符号链接(默认); -0, --null 将每个空行视作0 字节而非换行符; -S, --separate-dirs 不包括子目录的占用量; -s, --summarize 只分别计算命令列中每个参数所占的总用量; -x, --one-file-system 跳过处于不同文件系统之上的目录; -X, --exclude-from=文件 排除与指定文件中描述的模式相符的文件; -D, --dereference-args 解除命令行中列出的符号连接; --files0-from=F 计算文件F中以NUL结尾的文件名对应占用的磁盘空,如果F 的值是"-",则从标准输入读入文件名。 ``` 如上为Linux初学者必备命令,当然Linux命令还有很多,后面章节会随时学习新的命令。 ### 17 vi|vim编辑器 vi是一个命令行界面下的文本编辑工具,最早在1976年由Bill Joy开发,当时名字叫做ex。vi支持绝大多数操作系统(最早在BSD上发布),并且功能已经十分强大 1991年Bram Moolenaar基于vi进行改进,发布了vim,加入了对GUI的支持。 随着VIM更新发展,vim已经不是普通意义上的文本编辑器,而是被广泛的作为在文本编辑、方本处理、代码开发等用途,Linux中主流的文本编辑器包括:vi、vim、sublime、emacs、light table、eclipse、gedit等。 vim强大的编辑能力中很大部分是来自于其普通模式命令。vim的设计理念是命令的组合。 - “5dd”5表示总共5行,删除光标所在后的5行,包含光标行; - “d$” $"代表行尾,删除到行尾的内容,包含光标; - “2yy”表示复制光标及后2行,包括光标行; - “%d” %代表全部或者全局,%d表示删除文本所有的内容,也即是清空文档所有的内容。 vim是一个主流开源的编辑器,其默认执行vim命令,会显示帮助乌干达贫困的孩子,如图为vim与键盘键位功能对应关系: ![img](https://mmbiz.qlogo.cn/mmbiz_png/CRR2ENjuXAwlKl2aWk0tXxUVbnTu2BSOdKAtbG7nC47y8UcbTkVrqFhBoUpjwFv4bdjOdJMvwCQRPWMoPSBw2Q/0?wx_fmt=png) #### 17.1 vim编辑器模式 Vim编辑器模式常用有三种,分别是: - 命令行模式; - 文本输入模式; - 末行模式。 vim是vi的升级版本,它是安装在Linux操作系统中的一个软件,官网为:[www.vim.org](http://www.vim.org/) 在Linux Shell终端下默认执行vim命令,按Enter键后: - 默认进入命令行模式; - 在命令行模式按i进入文本输入模式; - 按ESC进入命令行模式; - 按:进入末行模式。 #### 17.2 vim编辑器必备 vim 编辑器最强大的功能,就在于内部命令及规则使用,如下为vim编辑器最常用的语法及规则: ``` 命令行模式:可以删除、复制、粘贴、撤销,可以切换到输入模式,输入模式跳转至命令行模式:按ESC键。 yy 复制光标所在行; nyy 复制n行; 3yy 复制3行; p,P 粘贴; yw 复制光标所在的词组,不会复制标点符号; 3yw 复制三个词组; u 撤消上一次; U 撤消当前所有; dd 删除整行; ndd 删除n行; x 删除一个字符; u 逐行撤销; dw 删除一个词组; a 从光标所在字符后一个位置开始录入; i 从光标所在字符前一个位置开始录入; I 从光标所在行的行首开始录入; o 跳至光标所在行的下一行行首开始录入; O 跳至光标所在行的上一行行首开始录入; 末行模式主要功能包括:查找、替换、末行保存、退出等; :w 保存; :q 退出; :s/x/y 替换1行; :wq 保存退出; 1,5s/x/y 替换1,5行; :wq! 强制保存退出; 1,$sx/y 从第一行到最后一行; :q! 强制退出; :x 保存; /word 从前往后找,正向搜索; ?word 从后往前走,反向搜索; :s/old/new/g 将old替换为new,前提是光标一定要移到那一行; :s/old/new 将这一行中的第一次出现的old替换为new,只替换第一个; :1,$s/old/new/g 第一行到最后一行中的old替换为new; :1,2,3s/old/new/g 第一行第二行第三行中的old改为new; ``` > ​ 欢迎关注我的微信公众号【**超哥的IT私房菜**】获取更多技术干货! > > ![image-20210707074326947](https://superman-1306409616.cos.ap-beijing.myqcloud.com/imageimage-20210707074326947.png) > > ​ 有什么吐槽或反馈意见,直接告诉我! > ​ 我会解决您说的问题,进一步更好的服务您哦!

优秀的个人博客,低调大师

Flask框架基础全面教程(2021实战版)

环境 centos7python3.6 环境部署 yum install python3pip3 install flask 项目创建 mkdir newprojcd newproj # cat > Hello_development.py <<EOF ###开发模式 from flask import Flask app = Flask(__name__) app.debug = True ##开启是可以实时查看 @app.route('/') def hello_world(): return 'Hello World' if __name__ == '__main__': app.run('0.0.0.0',5000) EOF # cat > Hello_production.py <<EOF ###生产模式 from flask import Flask from wsgiref.simple_server import make_server app = Flask(__name__) @app.route('/') def hello_world(): return "hello" @app.route('/web') def hello_world2(): return "hello web" if __name__ == '__main__': server = make_server('0.0.0.0', 5000, app) server.serve_forever() EOF # python3 Hello_production.py 浏览器访问http://ip:5000 Flask 路由 @app.route('/hello') def hello_world(): return 'hello world' http://localhost5000/hello Flask 变量规则 from flask import Flask app = Flask(__name__) @app.route('/hello/<name>') ##字符串变量 def hello_name(name): return 'Hello %s!' % name @app.route('/blog/<int:postID>') ##int型变量 def show_blog(postID): return 'Blog Number %d' % postID @app.route('/rev/<float:revNo>') ##浮点型变量 def revision(revNo): return 'Revision Number %f' % revNo if __name__ == '__main__': app.run(debug = True) 打开浏览器并输入URL http://localhost:5000/hello/feko如果输入http://localhost:5000/hello/feko/ 就会报404 规范URL Flask的URL规则基于Werkzeug的路由模块。这确保形成的URL是唯一的,并且基于Apache规定的先例。 from flask import Flask app = Flask(__name__) @app.route('/hello/<name>/') ##字符串变量 def hello_name(name): return 'Hello %s!' % name @app.route('/blog/<int:postID>/') ##int型变量 def show_blog(postID): return 'Blog Number %d' % postID @app.route('/rev/<float:revNo>/') ##浮点型变量 def revision(revNo): return 'Revision Number %f' % revNo if __name__ == '__main__': app.run(debug = True) 打开浏览器并输入URL http://localhost:5000/hello/feko或 http://localhost:5000/hello/feko/ 都不会报错 Flask URL构建 URL重定向 from flask import Flask, redirect, url_for app = Flask(__name__) @app.route('/admin/') def hello_admin(): return 'Hello Admin' @app.route('/guest/<guest>/') def hello_guest(guest): return 'Hello %s as Guest' % guest @app.route('/user/<name>/') def hello_user(name): if name =='admin': return redirect(url_for('hello_admin')) else: return redirect(url_for('hello_guest',guest = name)) if __name__ == '__main__': app.run(debug = True) 打开浏览器并输入URL - http://localhost:5000/user/admin浏览器中的应用程序响应是:Hello Admin在浏览器中输入以下URL - http://localhost:5000/user/feko 应用程序响应现在更改为:Hello feko as Guest HTTP方法 GET - 从指定的资源请求数据。以未加密的形式将数据发送到服务器。最常见的方法。POST - 向指定的资源提交要被处理的数据,用于将HTML表单数据发送到服务器。login.html <html> <body> <form action = "http://192.168.209.211:5000/login/" method = "post"> <p>Enter Name:</p> <p><input type = "text" name = "nm" /></p> <p><input type = "submit" value = "submit" /></p> </form> </body> </html> Hello_development.py from flask import Flask, redirect, url_for, request app = Flask(__name__) @app.route('/success/<name>/') def success(name): return 'welcome %s' % name @app.route('/login/',methods = ['POST', 'GET']) ##action 地址要对应,有'/'也有'/' def login(): if request.method == 'POST': user = request.form['nm'] return redirect(url_for('success',name = user)) else: user = request.args.get('nm') return redirect(url_for('success',name = user)) if __name__ == '__main__': app.run(debug = True) Flask 模板使用 render_template 函数用于渲染模板模板必须放在templates目录下Hello_development.py from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): my_int = 1 my_str = 'fafa' my_list = [1,2,3,4,5,6] my_dict = { 'name': 'feko', 'age': 26 } # render_template方法:渲染模板 # 参数1: 模板名称 参数n: 传到模板里的数据 return render_template('hello.html', my_int=my_int, my_str=my_str, my_list=my_list, my_dict=my_dict ) if __name__ == '__main__': app.run(debug=True) cat templates/hello.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>我是模板</h3> {{ my_int }} <br> {{ my_str }} <br> {{ my_list }} <br> {{ my_dict }} <hr> <h3>两种方法:模板的list数据获取</h3> <hr> {{ my_list[0] }} <br> {{ my_list.1 }} <hr> <h3>两种方法:字典数据获取</h3> <hr> {{ my_dict['name'] }} <br> {{ my_dict.age }} <hr> <h3>两种方法:算术运算</h3> <br> {{ my_list.0 + 10 }} <br> {{ my_list[0] + my_list.1 }} </body> </html> Flask 静态文件静态文件主要包括js/css文件,并存在static目录下python程序去渲染html模板,模板再去调用js里面的函数Hello_development.py from flask import Flask, render_template app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") if __name__ == '__main__': app.run(debug = True) templates/index.html <html> <head> <script type = "text/javascript" src = "{{ url_for('static', filename = 'hello.js') }}" ></script> </head> <body> <input type = "button" onclick = "sayHello()" value = "Say Hello" /> </body> </html> static/hello.js function sayHello() { alert("Hello World") } #Flask Request对象来自客户端网页的数据作为全局请求对象发送到服务器。为了处理请求数据,应该从Flask模块导入。Request对象的重要属性如下所列:Form - 它是一个字典对象,包含表单参数及其值的键和值对。args - 解析查询字符串的内容,它是问号(?)之后的URL的一部分。Cookies - 保存Cookie名称和值的字典对象。files - 与上传文件有关的数据。method - 当前请求方法。 Flask 将表单数据发送到模板 cat Hello_development.py ##主程序 from flask import Flask, render_template, request app = Flask(__name__) @app.route('/') def student(): return render_template('student.html') @app.route('/result',methods = ['POST','GET']) def result(): if request.method == 'POST': result = request.form return render_template('result.html',result = result) if __name__ == '__main__': app.run('0.0.0.0',5000,debug = True) cat templates/student.html ##脚本 <form action="http://192.168.209.211:5000/result" method="POST"> <p>Name <input type = "text" name = "Name"/></p> <p>Physics <input type = "text" name = "Physics"/></p> <p>Maths <input type = "text" name = "Maths"/></p> <p><input type = "submit" value="submit"/></p> </form> cat templates/result.html ##模板 <!doctype html> <table border = 1> {% for key, value in result.items() %} <tr> <th>{{key}}</th> <th>{{value}}</th> </tr> {% endfor %} </table> Flask Cookies Cookie以文本文件的形式存储在客户端的计算机上。其目的是记住和跟踪与客户使用相关的数据,以获得更好的访问者体验和网站统计信息。Hello_development.py from flask import Flask, make_response, request app = Flask(__name__) @app.route('/set_cookies') def set_cookies(): resp = make_response("success") resp.set_cookie("cookie_id_1","009911", max_age=3600) return resp @app.route('/get_cookies') def get_cookies(): cookies_get = request.cookies.get('cookie_id_1') return cookies_get @app.route('/delete_cookies') def delete_cookies(): resp = make_response('del success') resp.delete_cookie('cookie_id_1') return resp if __name__ == '__main__': app.run('0.0.0.0',5000,debug=True) Flask Sessions(会话) Session(会话)数据存储在服务器上。会话是客户端登录到服务器并注销服务器的时间间隔。会话变量使用cat Hello_development.py #!/usr/bin/env ptyoh # ~*~ coding: utf-8 ~*~ from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) app.secret_key = '9988aabbkkllii' @app.route('/') def index(): if 'username' in session: return "登录用户是:" + session["username"] + "<br><a href='/logout'>点击这里注销登录</a>" return ''' 你暂时没有登录 <br> <a href='/login'>点击这里登录</a> ''' @app.route('/login',methods = ['POST','GET']) def login(): if request.method == 'POST': session["username"] = request.form["username"] ##可以直接当作dict使用 return redirect(url_for('index')) return ''' <form action = '' method = 'POST'> <p>username:<input type='text' name = 'username' <br></p> <p><input type='submit' value = 'submit'</p> </form> ''' @app.route('/logout') def logout(): session.pop('username',None) return redirect(url_for('index')) if __name__ == '__main__': app.run('0.0.0.0',5000,debug = True) Flask 重定向和错误 重定向Flask.redirect(location, statuscode, response)在上述函数中:location参数是应该重定向响应的URL。statuscode发送到浏览器标头,默认为302。response参数用于实例化响应。错误Flask类具有带有错误代码的abort()函数。 Flask.abort(code)code参数采用以下值之一:400 - 用于错误请求401 - 用于未身份验证的403 - Forbidden404 - 未找到406 - 表示不接受415 - 用于不支持的媒体类型429 - 请求过多 Hello_development.py #!/usr/bin/env ptyoh # ~*~ coding: utf-8 ~*~ from flask import Flask, session, redirect, url_for, escape, request,render_template,abort app = Flask(__name__) @app.route('/') def index(): return render_template('log_in.html') @app.route('/login',methods = ['POST','GET']) def login(): if request.method == 'POST': if request.form["username"] == "admin": return redirect(url_for('success'),301) else: abort(401) else: return redirect(url_for('index')) @app.route('/success') def success(): return "login is success" if __name__ == '__main__': app.run('0.0.0.0',5000,debug = True) templates/log_in.html <form action="/login" method="post" > <p>username:<input type="text" name="username"/></p> <p><input type="submit" value="submit"/></p> </form> Flask 消息闪现 一个好的基于 GUI 的应用程序会向用户提供有关交互的反馈。简单使用:后端定义flash("login successful!!")模板获取get_flashed_messages() Hello_development.py #!/usr/bin/env ptyoh # ~*~ coding: utf-8 ~*~ from flask import Flask, session, redirect, url_for, escape, request,render_template,abort,flash import os app = Flask(__name__) app.secret_key=os.urandom(32) @app.route('/') def index(): return render_template('index.html') @app.route('/login',methods = ['POST','GET']) def login(): error = None if request.method == 'POST': if request.form['username'] != "admin" or request.form['password'] != "admin": error = "Invalid username or passworld!!!" else: flash("login is successful!!!") return redirect(url_for('index')) return render_template('login.html',error = error) if __name__ == '__main__': app.run('0.0.0.0',5000,debug = True) templates/index.html <html> <head> <meta charset="UTF-8"> <title>Index</title> </head> <body> {% with messages = get_flashed_messages() %} {% if messages %} {% for i in messages %} {{i}} {% endfor %} {% endif %} {% endwith %} <p>Do you want to <a href="{{ url_for('login') }}">log in?</a></p> </body> </html> templates/login.html <html> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <form action="/login" method="post"> <p>username:<input type="text" name="username"/></p> <p>password:<input type="text" name="password"/></p> <p><input type="submit" value="submit"/></p> </form> {% if error %} <p>ERROR:{{error}}</p> {% endif %} </body> </html> Flask 文件上传 在 Flask 中处理文件上传非常简单。它需要一个 HTML 表单,其 ​enctype​ 属性设置为“​multipart / form-data”​,将文件发布到 URL。URL 处理程序从 ​request.files[]​ 对象中提取文件,并将其保存到所需的位置。 Hello_development.py #!/usr/bin/env ptyoh # ~*~ coding: utf-8 ~*~ from flask import Flask, request,render_template from werkzeug.utils import secure_filename import os app = Flask(__name__) app.config['UPLOAD_FOLDER'] = 'upload/' @app.route('/upload') def upload(): return render_template('upload.html') @app.route('/upload_file',methods = ['POST','GET']) def upload_file(): if request.method == 'POST': f = request.files["file"] f.save(os.path.join(app.config['UPLOAD_FOLDER'],secure_filename(f.filename))) return "upload file is successful!!!" if __name__ == '__main__': app.run('0.0.0.0',5000,debug = True) templates/upload.html <html> <head> <meta charset="UTF-8"> <title>file upload</title> </head> <form action="/upload_file" method="post" enctype="multipart/form-data"> <p><input type="file" name="file" accept=".jpg,.png,.zip,.tar.gz"/> <input type="submit" value="submit"/></p> <p>注意:上传的文件名默认不支持中文命名</p> </form> </html> Flask扩展 Flask通常被称为微框架,因为核心功能包括基于Werkzeug的WSGI和路由以及基于Jinja2的模板引擎。此外,Flask框架还支持cookie和会话,以及JSON,静态文件等Web帮助程序。这不足以开发完整的Web应用程序。Flask扩展为Flask框架提供了可扩展性。Flask扩展是一个Python模块重要的Flask扩展:Flask Mail - 为Flask应用程序提供SMTP接口Flask WTF - 添加WTForms的渲染和验证Flask SQLAlchemy - 为Flask应用程序添加SQLAlchemy支持Flask Sijax - Sijax的接口 - Python/jQuery库,使AJAX易于在Web应用程序中使用 Flask扩展之邮件 Flask-Mail扩展使得与任何电子邮件服务器建立简单的接口变得非常容易,属于python模块,可以用pip安装。使用Mail类来配置邮箱和发送邮件,Message类来封装邮件.测试账号jkfeko94@163.combr/>使用Mail类来配置邮箱和发送邮件,Message类来封装邮件.测试账号jkfeko94@163.comjkfeko9488889999 #密码WGPJPPBTONSIQKCE # Hello_development.py from flask import Flask from flask_mail import Mail,Message app= Flask(__name__) ##定义邮件配置 app.config['MAIL_SERVER'] = 'smtp.163.com' app.config['MAIL_PORT'] = 465 app.config['MAIL_USERNAME'] = 'jkfeko94@163.com' app.config['MAIL_PASSWORD'] = 'WGPJPPBTONSIQKCE' app.config['MAIL_USE_TLS'] = False app.config['MAIL_USE_SSL'] = True ##创建实例 mail = Mail(app) @app.route('/') def index(): msg = Message('Flask Mail',sender=app.config['MAIL_USERNAME'],recipients=['1025158107@qq.com']) msg.body = "This is a flask mail send test.." mail.send(msg) return 'sent' if __name__ == '__main__': app.run('0.0.0.0',5000,debug=True) Flask扩展之WTF WTForms表单:Web应用程序的一个重要方面是为用户提供用户界面的扩展。如果通过html创建表单:表单元素必须定义两次 - 一次在HTML中,另一次在服务器端脚本中。用WTF就只创建一次即可。WTF两大部分wtforms标准表单字段:如 TextField 表示<input type ='text'> ##HTML表单元素validators验证器:如validators.DataRequired("不能为空") ##不输入会返回error="不能为空" forms.py from flask_wtf import FlaskForm from wtforms import RadioField,TextAreaField,SelectField,IntegerField,SubmitField,StringField from wtforms import validators, ValidationError import email_validator ##创建联系人的类,包括名字,性别,地址,邮箱,年龄,技能语言 ##用到WTF 必须继承FlaskForm类 class ContactForm(FlaskForm): name = StringField('姓名',[validators.DataRequired('姓名不能为空,请输入正常的名字!!!')]) gender = RadioField('性别',choices=[('M','男'),('F','女')]) address = TextAreaField("住址") email = StringField('邮箱',[validators.DataRequired('邮箱不能为空!!'),validators.Email("邮箱格式错误,请输入正确格式")]) age = IntegerField('年龄') language = SelectField('编程语言',choices=[('cpp','C++'),('py','python')]) submit = SubmitField('提交') Hello_development.py from flask import Flask,render_template,request,flash from forms import ContactForm import os app = Flask(__name__) app.secret_key = os.urandom(32) @app.route('/contact',methods=['POST','GET']) def contact(): form = ContactForm() #实例化联系人表单 if request.method == 'POST': if form.validate() == False: ##检查表单输入是否有误 flash("输入内容有误") return render_template('contact.html', form=form) else: name = form.name.data ##获取表单值 gender = form.gender.data email = form.email.data age = form.age.data language = form.language.data return str(name) + "<br>" + \ str(gender) + "<br>" +\ str(email) + "<br>" +\ str(age) + "<br>" +\ str(language) + "<br>" +\ "<br> post contact is successful!!!" return render_template('contact.html',form = form) if __name__ == "__main__": app.run('0.0.0.0',5000,debug=True) templates/contact.html <html> <head> <meta charset="UTF-8"/> <title>联系人表单填写</title> </head> <body> <h1>联系人表单填写 </h1> {% with msg = get_flashed_messages() %} {% if msg %} {% for i in msg %} {{ i }} {% endfor %} {% endif %} {% endwith %} {% for msg in form.name.errors %} <div>{{ msg }}</div> {% endfor %} {% for msg in form.email.errors %} <div>{{ msg }}</div> {% endfor %} {% for msg in form.age.errors %} <div>{{ msg }}</div> {% endfor %} <form action="/contact" method="post"> <fieldset> <legend>联系人表单填写内容</legend> <!-- 渲染所有隐藏字段,包括跨站请求伪造保护 --> {{ form.hidden_tag() }} <div style=" font-size:20px;font-weight:bold; margin-left: 600px;"> {{ form.name.label }} <br> {{ form.name }} <br> {{ form.gender.label }} {{ form.gender }} <br> {{ form.address.label }} <br> {{ form.address }} <br> {{ form.email.label }} <br> {{ form.email }} <br> {{ form.age.label }} <br> {{ form.age }} <br> {{ form.language.label }} <br> {{ form.language }} <br> {{ form.submit }} </div> </fieldset> </form> </body> </html> Flask扩展之SQLite3 创建sqlite数据库和表Create_Sqlite.py import sqlite3 con = sqlite3.connect('database.db') cur = con.cursor() cur.execute('create table contact (name TEXT, gender TEXT, email TEXT, age TEXT, language TEXT)') con.commit() con.close() Hello_development.py from flask import Flask,render_template,request,flash from forms import ContactForm import sqlite3 import os app = Flask(__name__) app.secret_key = os.urandom(32) @app.route('/') def home(): return render_template('home.html') @app.route('/contact',methods=['POST','GET']) def contact(): form = ContactForm() #实例化联系人表单 if request.method == 'POST': if form.validate() == False: ##检查表单输入是否有误 flash("输入内容有误") return render_template('contact.html', form=form) else: try: name = form.name.data gender = form.gender.data email = form.email.data age = form.age.data language = form.language.data with sqlite3.connect("database.db") as con: ##要先手动创建sqlite数据库和表 cur = con.cursor() cont = [(name,gender,email,age,language)] cur.executemany('INSERT INTO contact(name,gender,email,age,language) VALUES (?,?,?,?,?)', cont) con.commit() msg = "Record successfully added." except: con.rollback() #msg = "error record is fail." finally: con.close() return render_template('result.html',msg = msg) return render_template('contact.html',form = form) @app.route('/show') def show(): with sqlite3.connect("database.db") as con: ##要先手动创建sqlite数据库和表 con.row_factory = sqlite3.Row #将设置row_factory为callable sqlite3.Row,将普通元组转换为更有用的对象。 cur = con.cursor() cur.execute('select * from contact') rows = cur.fetchall() return render_template('show.html',rows = rows) if __name__ == "__main__": app.run('0.0.0.0',5000,debug=True) templates/home.html <html> <head> <meta charset="UTF-8"> <title>SQLITE3数据库的读写</title> </head> <h1>SQLITE3数据库的读写</h1> <p> <a href="/contact">点击进入联系人信息录入界面</a> </p> <br> <p> <a href="/show">点击进入联系人信息查看界面</a> </p> <body> </body> templates/contact.html <html> <head> <meta charset="UTF-8"/> <title>联系人表单填写</title> </head> <body> <h1>联系人表单填写 </h1> {% with msg = get_flashed_messages() %} {% if msg %} {% for i in msg %} {{ i }} {% endfor %} {% endif %} {% endwith %} {% for msg in form.name.errors %} <div>{{ msg }}</div> {% endfor %} {% for msg in form.email.errors %} <div>{{ msg }}</div> {% endfor %} {% for msg in form.age.errors %} <div>{{ msg }}</div> {% endfor %} <form action="/contact" method="post"> <fieldset> <legend>联系人表单填写内容</legend> <!-- 渲染所有隐藏字段,包括跨站请求伪造保护 --> {{ form.hidden_tag() }} <div style=" font-size:20px;font-weight:bold; margin-left: 600px;"> {{ form.name.label }} <br> {{ form.name }} <br> {{ form.gender.label }} {{ form.gender }} <br> {{ form.address.label }} <br> {{ form.address }} <br> {{ form.email.label }} <br> {{ form.email }} <br> {{ form.age.label }} <br> {{ form.age }} <br> {{ form.language.label }} <br> {{ form.language }} <br> {{ form.submit }} <br> <p> <a href="/">点击返回首页</a> </p> </div> </fieldset> </form> </body> </html> templates/show.html <html> <head> <meta charset="UTF-8"> <title>联系人信息查看界面</title> </head> <body> <h1>联系人信息查看界面</h1> <table border = 1> <thead> <td>Name</td> <td>gender</td> <td>gender</td> <td>age</td> <td>language</td> </thead> {% for i in rows %} <tr> <td>{{i["name"]}}</td> <td>{{i["gender"]}}</td> <td> {{ i["email"]}}</td> <td>{{i['age']}}</td> <td>{{i['language']}}</td> </tr> {% endfor %} </table> <p><a href="/">Go to home!!</a></p> </body> </html> templates/result.html <html> <head> <meta charset="UTF-8"> <title>SQLITE3数据库读写返回状态</title> </head> <h1>SQLITE3数据库读写返回状态</h1> {{ msg }} <br> <p><a href="/">Go to home!!</a></p> <body> </body> </html> Flask SQLAlchemy SQLAlchemy,Python工具包是一个强大的ORM,将内容存储在对象的方式模块安装SQLAlchemy 1.3版本Flask-SQLAlchemy 2.4.4版本 templates/home.html <html> <head> <meta charset="UTF-8"> <title>SQLAlchemy SQL工具包及对象关系映射(ORM)工具</title> </head> <body> <h1>SQLAlchemy SQL工具包及对象关系映射(ORM)工具</h1> <p> <a href="/newstu">点击进入学生信息录入界面</a> </p> <br> <p> <a href="/showstu">点击进入学生信息查看界面</a> </p> </body> </html> templates/newstu.html <html> <head> <meta charset="UTF-8"> <title>学生信息录入-Flask SQLAlchemy 使用</title> </head> <h1>学生信息录入-Flask SQLAlchemy 使用</h1> {% for msg in get_flashed_messages() %} {{ msg }} {% endfor %} <form action="{{ url_for('stu') }}" method="post"> <p>姓名:<input type="text" name="name"></p> <p>城市:<input type="text" name="city"></p> <p>地址:<textarea name="addr"></textarea></p> <p>手机号码:<input type="text" name="phone"></p> <p><input type="submit" value="submit"></p> </form> <a href="/">返回主页</a> </html> templates/showstu.html <html> <head> <meta charset="UTF-8"> <title>学生信息查询结果</title> </head> <h1>学生信息查询结果</h1> <table border = 1> <thead> <tr> <th>姓名</th> <th>城市</th> <th>地址</th> <th>手机号码</th> </tr> </thead> <tbody> {% for stu in students %} <tr> <td>{{stu.name}}</td> <td>{{stu.city}}</td> <td>{{stu.addr}}</td> <td>{{stu.phone}}</td> </tr> {% endfor %} </tbody> </table> <a href="/">返回主页</a> </html> Hello_development.py from flask import Flask,render_template,request,flash import os,pymysql from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.secret_key = os.urandom(32) # 设置连接sqlite3数据库的URL # app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///stu.db' # app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False # 设置连接mysql数据库的URL app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@192.168.5.157:3306/test' # 设置每次请求结束后会自动提交数据库的改动 app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True # 查询时显示原始SQL语句 #app.config['SQLALCHEMY_ECHO'] = True db = SQLAlchemy(app) ##实例化 class Students(db.Model): ##继承父类Model __tablename__ = 'students' id = db.Column('student_id', db.Integer, primary_key = True) name = db.Column(db.String(100)) city = db.Column(db.String(200)) addr = db.Column(db.String(100)) phone = db.Column(db.String(200)) def __init__(self, name, city, addr, phone): self.name = name self.city = city self.addr = addr self.phone = phone @app.route('/') def home(): #db.create_all() ##可以用这个来创建表结构一般创建完就注释的 return render_template('home.html') @app.route('/newstu') def newstu(): return render_template('newstu.html') @app.route('/showstu') def showstu(): return render_template('showstu.html',students = Students.query.all()) @app.route('/stu',methods=['POST']) def stu(): if request.method == 'POST': if not request.form['name'] or not request.form['city'] or not request.form['addr'] or not request.form['phone']: flash('Input cannot be empty') else: student = Students(request.form['name'],request.form['city'],request.form['addr'],request.form['phone']) db.session.add(student) db.session.commit() flash("Record was successfully added") return render_template('newstu.html') if __name__ == "__main__": app.run('0.0.0.0',5000,debug=True) Flask AJAX AJAX不是JavaScript的规范,它只是一个哥们“发明”的缩写:Asynchronous JavaScript and XML,意思就是用JavaScript执行异步网络请求。如果仔细观察一个Form的提交,你就会发现,一旦用户点击“Submit”按钮,表单开始提交,浏览器就会刷新页面,然后在新页面里告诉你操作是成功了还是失败了。如果不幸由于网络太慢或者其他原因,就会得到一个404页面。这就是Web的运作原理:一次HTTP请求对应一个页面。如果要让用户留在当前页面中,同时发出新的HTTP请求,就必须用JavaScript发送这个新请求,接收到数据后,再用JavaScript更新页面,这样一来,用户就感觉自己仍然停留在当前页面,但是数据却可以不断地更新。最早大规模使用AJAX的就是Gmail,Gmail的页面在首次加载后,剩下的所有数据都依赖于AJAX来更新。用JavaScript写一个完整的AJAX代码并不复杂,但是需要注意:AJAX请求是异步执行的,也就是说,要通过回调函数获得响应 templates/ajax.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ajax</title> <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script> <body> <form id="my_form"> name: <input name="name" type="text"/><br> age: <input name="age" type="text"/><br> <input id="my_button" type="button" value="button"/> </form> <script> $("#my_button").click(function () { $.ajax({ url:"/test_post", type:"POST", dataType:"json", async:true, data:$("#my_form").serialize(), success:function (result) { // 回调函数,处理app.py返回的结果 alert(result['ok']) }, error:function (XMLHttpRequest,textStatus,errorThrown) { alert(XMLHttpRequest.status) alert(XMLHttpRequest.readyState) alert(textStatus) } }); }) </script> </body> </html> Hello_development.py import os,json from flask import Flask,render_template,jsonify,request,redirect,url_for app = Flask(__name__) @app.route('/') def home(): return render_template('ajax.html') @app.route('/test_post',methods=['POST','GET']) def test_post(): if request.method == 'POST': name=request.form.get('name') age=request.form.get('age') print(name,age) return jsonify({'ok': True}) return redirect(url_for('home')) if __name__ == "__main__": app.run('0.0.0.0',5000,debug=True) 以上全部代码均实操通过,时间20210322

优秀的个人博客,低调大师

2020年12月-前端基础-表格 table

表格 table(会使用) 为了让我们页面显示的更加整齐,我们需要学习三个表(表格、表单、列表) 理解: 能说出表格用来做什么的 表格的基本结构组成 表格作用: 存在即是合理的。 表格的现在还是较为常用的一种标签,但不是用来布局,常见显示、展示表格式数据。 因为它可以让数据显示的非常的规整,可读性非常好。 特别是后台展示数据的时候表格运用是否熟练就显得很重要, 一个清爽简约的表格能够把繁杂的数据表现得很有条理 ,虽然 div 布局也可以做到,但是总没有表格来得方便。 ps: 这些地方用表格,你会觉得很贴切。 1. 创建表格 在HTML网页中,要想创建表格,就需要使用表格相关的标签。 创建表格的基本语法: <table> <tr> <td>单元格内的文字</td> ... </tr> ... </table> 要深刻体会表格、行、单元格他们的构成。 在上面的语法中包含基本的三对HTML标签,分别为 table、tr、td,他们是创建表格的基本标签,缺一不可,下面对他们进行具体地解释 1.`table`用于定义一个表格标签。 2.`tr`标签 用于定义表格中的行,必须嵌套在`table`标签中。 3.`td` 用于定义表格中的单元格,必须嵌套在`<tr></tr>`标签中。 4.字母`td`指表格数据(table data),即数据单元格的内容,现在我们明白,表格最合适的地方就是用来存储数据的。 总结: 表格的主要目的是用来显示特殊数据的 一个完整的表格有表格标签(table),行标签(tr),单元格标签(td)组成,没有列的标签 <tr></tr>中只能嵌套<td></td> 类的单元格 <td></td>标签,他就像一个容器,可以容纳所有的元素 2. 表格属性 表格有部分属性我们不常用,这里重点记住 cellspacing 、 cellpadding。 我们经常有个说法,是三参为0, 平时开发的我们这三个参数 border cellpadding cellspacing 为 0 案例1: <table width="500" height="300" border="1" cellpadding="20" cellspacing="0" align="center"> <tr> <th>姓名</th> <th>性别</th> <th>年龄</th> </tr> <tr> <td>刘德华</td> <td>男</td> <td>55</td> </tr> <tr> <td>郭富城</td> <td>男</td> <td>52</td> </tr> <tr> <td>张学友</td> <td>男</td> <td>58</td> </tr> <tr> <td>黎明</td> <td>男</td> <td>18</td> </tr> <tr> <td>刘晓庆</td> <td>女</td> <td>63</td> </tr> </table> 3. 表头单元格标签th 作用: 一般表头单元格位于表格的第一行或第一列,并且文本加粗居中 语法: 只需用表头标签<th></th>替代相应的单元格标签<td></td>即可。 案例2: 效果图 代码: <table width="500" border="1" align="center" cellspacing="0" cellpadding="0"> <tr> <th>姓名</th> <th>性别</th> <th>电话</th> </tr> <tr> <td>小王</td> <td>女</td> <td>110</td> </tr> <tr> <td>小明</td> <td>男</td> <td>120</td> </tr> </table> th 也是一个单元格 只不过和普通的 td单元格不一样,它会让自己里面的文字居中且加粗 4. 表格标题caption 定义和用法 <table> <caption>我是表格标题</caption> </table> **注意: ** 1.`caption` 元素定义表格标题,通常这个标题会被居中且显示于表格之上。 2.`caption` 标签必须紧随 table 标签之后。 3.这个标签只存在表格里面才有意义。 5. 合并单元格(难点) 合并单元格是我们比较常用的一个操作,但是不会合并的很复杂。 5.1 合并单元格2种方式 跨行合并:rowspan="合并单元格的个数" 跨列合并:colspan="合并单元格的个数" 5.2 合并单元格顺序 合并的顺序我们按照 先上 后下 先左 后右 的顺序 5.3 合并单元格三步曲 1.先确定是跨行还是跨列合并 2.根据 先上 后下 先左 后右的原则找到目标单元格 然后写上 合并方式 还有 要合并的单元格数量 比如 : `<td colspan="3"> </td>` 3.删除多余的单元格 单元格 6. 总结表格 标签名 定义 说明 <table></table> 表格标签 就是一个四方的盒子 <tr></tr> 表格行标签 行标签要再table标签内部才有意义 <td></td> 单元格标签 单元格标签是个容器级元素,可以放任何东西 <th></th> 表头单元格标签 它还是一个单元格,但是里面的文字会居中且加粗 <caption></caption> 表格标题标签 表格的标题,跟着表格一起走,和表格居中对齐 clospan 和 rowspan 合并属性 用来合并单元格的 1.表格提供了HTML 中定义表格式数据的方法。 2.表格中由行中的单元格组成。 3.表格中没有列元素,列的个数取决于行的单元格个数。 4.表格不要纠结于外观,那是CSS 的作用。 5.表格的学习要求: 能手写表格结构,并且能简单合并单元格。 7. 拓展阅读@ 表格划分结构(了解) 对于比较复杂的表格,表格的结构也就相对的复杂了,所以又将表格分割成三个部分:题头、正文和脚注。而这三部分分别 用:thead,tbody,tfoot来标注, 这样更好的分清表格结构 注意: 1.`<thead></thead>:`用于定义表格的头部。用来放标题之类的东西。`<thead>` 内部必须拥有 `<tr>` 标签! 2.`<tbody></tbody>:`用于定义表格的主体。放数据本体 。 3.`<tfoot></tfoot>: `放表格的脚注之类。 4.以上标签都是放到table标签中。

优秀的个人博客,低调大师

图像处理基础:颜色空间及其OpenCV实现

点击上方“AI公园”,关注公众号,选择加“星标“或“置顶” 作者:Soumyadip Sarkar 编译:ronghuaiyang 导读 对图像的颜色空间做了一个概念性的介绍,并通过代码的方式可视化了每种颜色空间的每个通道所表示的意义。 文章内容包括: 什么是颜色空间? 颜色空间有哪些类别? 如何在OpenCV中实现? 什么是颜色空间? 颜色是一种连续的现象,它意味着有无数种颜色。但是,人类的眼睛和感知能力是有限的。所以,为了识别这些颜色,我们需要一种媒介或这些颜色的表示,这种颜色的表示被称为色彩空间。在技术术语中,一个颜色模型或颜色空间是一个特定的3-D坐标系统以及该系统中的一个子空间,其中每一种颜色都由一个单点表示。 有哪些颜色空间的类型? 目前主要有五种类型的颜色模型。但是,我将只写一些常见的(RGB、HSV和HSL)。 RGB(Red Green Blue) HSL(Hue Saturation Lightness) HSV(Hue Saturation Value) YUV(Luminance, blue–luminance, red–luminance) CMYK(Cyan, Magenta, Yellow, Key) RGB颜色空间: RGB颜色空间是三维坐标系中红、绿、蓝坐标所表示的著名颜色空间之一。在更专业的术语中,RGB将颜色描述为由三个部分组成的元组。每个部分都可以取0到255之间的值,其中元组(0,0,0)表示黑色,元组(255,255,255)表示白色。元组的第0、第1和第2个部分分别表示红、绿、蓝的分量。 RGB颜色空间的Python实现: 这里我们导入了必要的库,cv2用于颜色空间转换,NumPy用于数组操作,Matplotlib用于显示图像,os用于访问图像目录,tqdm用于显示加载栏。 hsl_img=cv2.cvtColor(X[0],cv2.COLOR_BGR2HLS)####CONVERTINGBGRCOLORSPACEINTOHSLCOLORSPACE####hsl_img_1=hsl_img.copy()hsl_img_2=hsl_img.copy()hsl_img_3=hsl_img.copy()hsl_img_1[:,:,1]=0####HUE-->ZERO####hsl_img_1[:,:,2]=0hsl_img_2[:,:,0]=0####SATURATION-->ZERO####hsl_img_2[:,:,2]=0hsl_img_3[:,:,0]=0####LIGHTNESS-->ZERO####hsl_img_3[:,:,1]=0 设置两个空列表Z和X,分别用于存储带有各自图像的标签,然后指定图像大小和路径目录。在这之后,我定义了两个函数,用于返回flower类型(assign_lable)和访问每个图像、读取和调整其大小(make_train_data)。 Z,X=[],[]IMG_SIZE=150FLOWER_SUNFLOWER_DIR='../input/flowers-recognition/flowers/flowers/sunflower'defassign_label(img,flower_type):returnflower_typedefmake_train_data(flower_type,DIR):forimgintqdm(os.listdir(DIR)):label=assign_label(img,flower_type)path=os.path.join(DIR,img)img=cv2.imread(path,cv2.IMREAD_COLOR)img=cv2.resize(img,(IMG_SIZE,IMG_SIZE))#Resizingtheimage 加载图像,然后在OpenCV以BGR格式读取图像时将BGR颜色空间转换为RGB颜色空间,但Maplotlib使用RGB格式来显示图像。这就是为什么我们需要转换颜色空间后,读取图像为RGB。 然后对固定图像进行三份拷贝,并将每份拷贝的任何双色通道设为零,分别用于访问红、绿、蓝通道。如果你让第0个颜色通道都是0那么你只会得到蓝色通道。 make_train_data('Sunflower',FLOWER_SUNFLOWER_DIR)#####LoadingSunflowerDatafix_img=cv2.cvtColor(X[0],cv2.COLOR_BGR2RGB)###########CONVERTINGBGRCOLORSPACEINTORGBCOLORSPACE#########new_img_1=fix_img.copy()new_img_2=fix_img.copy()new_img_3=fix_img.copy()new_img_1[:,:,0]=0#makingRchannelzero####ForBLUEchannel#####new_img_1[:,:,1]=0#makingGchannelzeronew_img_2[:,:,1]=0####ForREDcolorChannel####new_img_2[:,:,2]=0new_img_3[:,:,0]=0###ForGREENChannel####new_img_3[:,:,2]=0 显示图像: f,axes=plt.subplots(1,3,figsize=(15,15))list=[new_img_1,new_img_2,new_img_3]i=0foraxinaxes:ax.imshow(list[i])i+=1 HSL颜色空间: HSL的一般含义是色调、饱和度和明度。你可以将HSL以圆柱体的形式可视化,如图2(a)所示。围绕圆柱体的是不同的颜色,比如绿色、黄色、红色等等(我们真正想要的颜色)。饱和度是指颜色的多少,而明度是指颜色有多暗或多亮。正如你所看到的,圆柱体的顶部全是白色,底部全是黑色。 图2:HSL颜色空间 HSL颜色空间的Python实现: 使用OpenCV函数**cvtColor()**将BGR颜色空间转换为HSL颜色空间,在这里我们需要传递图像,以及从哪个颜色空间到哪个颜色空间我们想要改变图像。然后再复制并使两个颜色通道为零,以便分别显示每个颜色通道。 hsl_img=cv2.cvtColor(X[0],cv2.COLOR_BGR2HLS)####CONVERTINGBGRCOLORSPACEINTOHSLCOLORSPACE####hsl_img_1=hsl_img.copy()hsl_img_2=hsl_img.copy()hsl_img_3=hsl_img.copy()hsl_img_1[:,:,1]=0####HUE-->ZERO####hsl_img_1[:,:,2]=0hsl_img_2[:,:,0]=0####SATURATION-->ZERO####hsl_img_2[:,:,2]=0hsl_img_3[:,:,0]=0####LIGHTNESS-->ZERO####hsl_img_3[:,:,1]=0 现在显示三个不同的颜色通道→ f,axes=plt.subplots(1,3,figsize=(15,15))list=[hsl_img_1,hsl_img_2,hsl_img_3]i=0foraxinaxes:ax.imshow(list[i])i+=1 HSV颜色空间: HSV这个名字来自于颜色模型的三个坐标,即色相、饱和度和值。它也是一个圆柱形的颜色模型,圆柱体的半径表示饱和度,垂直轴表示值,角度表示色调。对于观察者,色调是占主导地位的,饱和度是混合到色调中的白光的数量,value是chrome的强度,value较低颜色变得更加类似于黑色,value越高,颜色变得更加像颜色本身。通过改变这些参数,我们可以生成不同的颜色。 图3:HSV颜色空间 HSV颜色空间的Python实现: 使用cvtColor()函数将色彩空间转换为HSV色彩空间。然后再复制并使两个通道置为零,以便分别显示每个通道。 hsv_img=cv2.cvtColor(X[0],cv2.COLOR_BGR2HSV)hsv_img_1=hsv_img.copy()hsv_img_2=hsv_img.copy()hsv_img_3=hsv_img.copy()hsv_img_1[:,:,1]=0#HUE-->ZEROhsv_img_1[:,:,2]=0hsv_img_2[:,:,0]=0#SATURATION-->ZEROhsv_img_2[:,:,2]=0hsv_img_3[:,:,0]=0#VALUE-->ZEROhsv_img_3[:,:,1]=0 单独显示每个颜色通道: —END— 英文原文:https://medium.com/analytics-vidhya/image-processing-series-part1-colorspaces-836d2e3ca700 请长按或扫描二维码关注本公众号 喜欢的话,请给我个在看吧! 本文分享自微信公众号 - AI公园(AI_Paradise)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册