2024年7月

MoneyPrinterPlus之前使用的是各种云厂商的语音识别服务来进行语音的视频和字幕的识别工作。

但是很多小伙伴说云服务用不起。

那么没办法,MoneyPrinterPlus上线最新版本,支持fasterWhisper本地语音识别模型。

赶紧来体验吧。

软件准备

当然,前提条件就是你需要下载MoneyPrinterPlus软件啦。

下载地址:
https://github.com/ddean2009/MoneyPrinterPlus

用得好的朋友,不妨给个star支持一下。 在软件v4.1版本之后,MoneyPrinterPlus已经支持fasterWhisper本地语音识别模型。

安装fasterWhipser的模型

fasterWhipser服务直接由MoneyPrinterPlus调用。所以不需要第三方的fasterWhisper服务。

但是我们需要下载对应的fasterWhipser模型到MoneyPrinterPlus中。

fasterWhisper模型下载地址:
https://huggingface.co/Systran

image-20240724104312078

可以看到里面有很多种模型,大家可以根据需要自行下载对应的模型。

怎么下载呢?

进入到MoneyPrinterPlus的fasterwhisper目录下:

cd fasterwhisper

执行git clone命令:

git clone https://huggingface.co/Systran/faster-whisper-tiny tiny

目前MoneyPrinterPlus支持下面几种模型名称:

'large-v3',  'large-v2', 'large-v1', 'distil-large-v3', 'distil-large-v2', 'medium', 'base', 'small', 'tiny'

所以你在git clone的时候,需要把faster-whisper仓库中的模型目录重命名为MoneyPrinterPlus支持的模型名称。

比如faster-whisper-tiny, 对应的模型叫做tiny,所以我们git clone的时候同时做了重命名操作:

git clone https://huggingface.co/Systran/faster-whisper-tiny tiny

上面的命令会在本地创建一个tiny的目录。目录里面包含了faster-whisper-tiny的所有模型内容。

在MoneyPrinterPlus中配置faster-whisper

我们启动MoneyPrinterPlus。

在基本配置区域:

image-20240724104858491

可以配置本地语音识别模型。

model name就是你下载下来的模型名字。

device type 可以选择cpu,cuda或者auto。

compute type 支持'int8','int8_float16','float16'这几种类型。

配置好之后,在AI视频区域。

语音识别配置中我们选择本地模型,即可使用到fasterWhisper了。

image-20240724105725203

同样的在视频混剪区域,我们也可以选择本地模型,即可使用到fasterWhisper了。

总结

因为是本地运行的fasterWhisper,所以在运行中可能会出现一些环境的问题。大家可以参考fasterWhisper的说明来解决。

点我查看更多精彩内容:www.flydean.com

VMware Cloud Foundation 使用 VMware Cloud Builder 工具完成自动化以及标准化的部署,除了要准备必须的用于部署管理域并运行管理相关组件的 ESXi 主机以外,还要准备一个用于部署管理域的预定义 Excel 参数表,这个参数主要用来指定部署环境中的相关信息,如 ESXi 主机、网络以及许可证密钥等,它在 Bring-up 过程当中起到了关键性作用。Excel 参数表分为两类,分别是 vcf-ems-deployment-parameter.xlsx 和 vcf-vxrail-deployment-parameter.xlsx 参数表,前者用于标准化 VCF 部署,后者主要用于
Dell Vxrail
产品配套使用,有点类似于 DIY 和 OEM 的区别,在实际部署当中主要会用到第一个。这两个参数表会随 VMware Cloud Builder OVA 文件一同在 Brondcom 支持门户发布,如果你不知道,也可以到我的另外一篇文章中(
VMware Cloud Foundation Part 02:部署 Cloud Builder。
)去进行下载。

之所以我会先说部署 VCF 管理域的 Excel 参数表,而不先说用于部署 VCF 管理域的 ESXi 主机,是因为我觉得在部署 VCF 之前应该先了解需要准备什么东西,以及 VCF 根据这些东西是怎么设计的,特别是对于还不了解 VCF 这个解决方案的小伙伴来说,当知道这些以后然后再反过准备满足 VCF 部署的环境,这样也能在真正的部署过程中规避许多问题。这在实际项目中,就好比先了解项目需求并做好规划设计,然后再循序渐进的完成项目建设。扯远了,其实还是建议将此文章与另外一篇文章(
VMware Cloud Foundation Part 04:准备 ESXi 主机。
)结合进行食用,效果更佳。话不多说,下面来看看这个 Excel 参数表到底有那些东西。

一、Introduction

打开 Excel 参数表(vcf-ems-deployment-parameter.xlsx),第一个表是关于这个 Excel 参数表的 Introduction 介绍。当前 Excel 参数表的版本是 v5.1.0,用于 VMware Cloud Builder 工具导入并进行部署 VCF 管理域。其中,有三个参数表,分别是 Credentials、Hosts and Networks 以及 Deploy Parameters,并解释了其含义和用途。注意事项,如果参数表中的单元格是黄色字段代表示例值,你需要修改为你部署环境的信息;如果参数表中的单元格变成红色则代表该字段是必填项并且值为空或者值有误;同时尽量避免在参数表中单元格之间使用复制和粘贴,有可能会导致在部署的时候无法验证,如果真要使用,请选择无格式粘贴。

二、Credentials

第一个真正的参数表(Credentials),这个参数表用来定义 VCF 管理域核心组件的用户凭据信息,其中包含 ESXi 主机、vCenter Server、NSX Manager、SDDC Manager 等。需要为这些组件的默认用户填入密码,比如 ESXi 主机是我们提前准备好的,默认超级用户 root 的密码也是我们安装的时候配置的,注意用于 VCF 管理域的所有 ESXi 主机的密码都应该一样。而其他组件是我们即将要部署 VCF 管理域时所创建的组件,所以我们需要给这些组件的默认用户预先定义好密码,后续在管理使用时也是用的这里所设置的密码。在红色单元格里填入这些组件的密码,注意设置的密码需要满足
复杂度要求

三、Hosts and Networks

下面是 Hosts and Networks 参数表,这个表是规划和设计 VCF 管理域时的重中之重,只要理解了这里的网络设计,那后面部署 VCF 时应该就比较简单了。这个表主要分为三个部分,分别是
Management Domain Networks

Management Domain ESXi
Hosts 和
NSX Host Overlay Network - DHCP/STATIC

在了解 Hosts and Networks 参数表之前,先来看一下 VCF 的网络设计,如下图所示。在 VCF 管理域中有 4 台 ESXi 主机,这是组成管理域计算节点最少的主机。VCF 管理域根据流量的类型将网络分为四类,分别是管理网络、vMotion 网络、vSAN 网络、NSX 网络,管理网络又分为主机(Host)管理网络和虚拟机(VM)管理网络。

  • 管理(Host 和 VM)网络

主机(Host)管理网络是指 ESXi 主机管理所使用的网络,安装 ESXi 主机时默认会将主机的一张网卡作为管理地址进行使用,如果你安装过 ESXi 应该非常清楚,安装完 ESXi 以后通过主机的 DCUI 进行配置 ESXi 管理所使用的网卡、IP地址以及 DNS 等,然后登录到 ESXi Host Client UI 管理界面,在网络视图中可以看到默认会创建一个标准交换机 vSwitch0,在这个交换机下有一个 Management Network 端口组,而端口组下面有一个 VMkernel 网卡配置了主机管理 IP 地址,这个网络就是主机管理网络。

虚拟机(VM)管理网络是指 VCF 管理域相关组件虚拟机(如vCenter Server、NSX Manager、SDDC Manager)所使用的管理网络,这些组件可以使用单独的管理网络进行管理,也可以和主机(Host)管理网络放在一起。

  • vMotion 网络

VCF 管理域中通常是用来放置管理域相关组件虚拟机,而不应该运行其他工作负载,这里的 vMotion 网络不是给工作负载虚拟机进行迁移使用的,而是给这些管理域相关组件虚拟机进行迁移使用的。

  • vSAN 网络

构建 VCF 管理域所使用的唯一主要存储是 vSAN,而 vSAN 分布式存储要求主机之间的存储流量使用单独网络进行传输。通常使用 L2 二层网络进行传输,使用传统的以太网交换机而不是 FC。

  • NSX 网络

NSX 网络分为两类,一类是 TEP 网络,这是组建 NSX 主机所使用的 VLAN 网络,通常主机有多少张网卡用于组网 NSX,就需要多少个 TEP IP地址,以前 NSX 版本支持标准交换机(VSS),现在只支持分布式交换机(VDS);另一类是覆盖网络,这是专用于传输 NSX Overlay Geneve 流量的专用网络,有点类似于 vSAN 专用存储流量网络的意思,不过这是一种隧道技术,每个网络(段)一个分段(Segment),通过 Overlay 实现大二层网络互通。在了解这些网络的含义和作用之前,可能你得先熟悉 NSX 网络的概念。

上述网络基本能实现虚拟化数据中心内的东西向流量互通,其中 NSX 是 VMware 在 VCF 解决方案中网络层面的大动脉,承接着各个核心组件以及工作负载之间的连接。如果想实现完整的 SDDC 的 VCF 解决方案,还需要用到 NSX 的 Edge 节点,如下图所示,这个节点的作用是将 NSX 网络中的工作负载连接到真实的物理环境中去实现南北向流量的互通,让外部的终端也能访问到 NSX 网络内部当中的服务。这个组件不会在初始构建(Bring-up)中进行设置,而是在完成管理域构建以后,再通过 SDDC Manager 进行配置。

在了解 VCF 的网络架构以后,现在回到 Hosts and Networks 参数表中来。

1)Management Domain Networks

第一部分是 Management Domain Networks 参数的配置。下图是定义 VCF 所使用的三种网络的信息,分别是管理网络、vMotion 网络和 vSAN 网络。管理网络包含下图中的主机管理网络(Management Network)和虚拟机管理网络(VM Management Network),可以将这两个网络使用 VLAN 进行分开,设置不同的网络/掩码和不同的网关,分属于不同的端口组,并设置 MTU 的大小,也可以放在一块,使用相同的 VLAN 网段,使用相同的网关。这个管理网络最终会被放在分布式交换机下面(下面会说),包括主机管理网络(ESXi 默认会有一个 vSwitch0,最终会被迁移到下面定义的分布式交换机下面然后被删除)。vMotion 网络和 vSAN 网络类似于上面所说的管理网络,都是不同的 VLAN 以及不同的网络,注意端口组的 MTU 大小设置了 9000,那到上层一直到真实的物理环境下,ESXi 主机网卡所连接的交换机等全部都得设置这个值,不然此设置无效。

下面是 VCF 根据网卡所承载的不同流量类型定义了三种 Profile 配置文件来创建分布式交换机(VDS),分别是 Profile-1、 Profile-2、 Profile-3。你可以点击“vSphere Distributed Switch Profile”后面的单元格中的选项进行选择不同的 Profile 配置类型,下面来看看这几种 Profile 配置类型分别是如何设定的。

第一种,
Profile-1
。在最下面有对这个 Profile 配置类型的解释,只创建一个分布式交换机,通常是物理 ESXi 主机只有两个网口用于 VCF 的时候进行选择,这个分布式交换机用于承载管理网络、vMotion 网络、vSAN 网络以及 NSX Overlay 网络的所有流量,但是肯定是要用 VLAN 进行分开,参考上面那张图。然后再配置这个分布式交换机的名字(Name),ESXi 主机分配给这个分布式交换机的物理网卡名称(pNICs),这个网口的名称需要根据实际 ESXi 主机上面的情况进行填写,然后是配置这个分布式交换机的 MTU 值(Size)以及传输网络的类型(Transport Zone Type),这个传输网络的类型支持选择,可以是 n/a(无)、VLAN、Overlay 以及 Overlay/VLAN。如果是下面选择了 Profile-1,也就是所有流量都跑在同一个分布式交换机上面,那就只能选择 Overlay/VLAN 了,因为 NSX 也需要跑在上面。

第二种,
Profile-2
。大多数真实环境中,ESXi 主机通常都有多个网口,Profile-2 是为使用 4 个网口所做的配置类型。当选择了 Profile-2 以后,定义了需要创建 2 个分布式交换机,每个分布式交换机分配 2 个网口,可以看到红色单元格提示我们需要设置另外一个分布式交换机的名字以及传输网络的类型。当选择这种 Profile 以后,不同分布式交换机就要分配不同的网络传输类型了,比如下图最下面说明了,分布式交换机 vds01 用于传输管理网络、vMotion 网络和 NSX Overlay 网络,配置选项里选择了 Overlay/VLAN 流量类型;而第二个分布式交换机(还没有取名字),专用于承载 vSAN 网络。注意,两个分布式交换机只能有一个分布式交换机用于承载 NSX Overlay 网络,比如说上面 vds01 选择了 Overlay/VLAN ,那下面另外一个分布式交换机就不能选择 Overlay/VLAN 或者 Overlay 了,只能选择 VLAN 或者 n/a。

这个其实需要根据实际情况进行选择,比如下图中,vds01 选择 VLAN 流量传输类型,用来跑管理网络和 vMotion 网络;vds02 选择 Overlay/VLAN 流量传输类型,用来跑 vSAN 网络和 NSX Overlay 网络。注意,这个地方也不是随便选的,如何正确搭配可以选择单元格中的不同选项来进行查看。

第三种,
Profile-3
。这种 Profile 配置类型也是为使用 4 个网口所做的,区别还是在于不同的分布式交换机所承载的网络类型不一样。这个配置类型只能将 vds02 交换机用作 NSX Overlay 网络,vds01 交换机用作管理网络、vMotion 网络以及vSAN 网络。

上面有一些啰嗦,不过还是希望能正确区别不同的网络类型以及不同的 Profile 配置类型的对应关系,以减少出错率。

2)Management Domain ESXi Hosts

下面这部分是用于配置管理域 ESXi 主机以及三种网络类型的参数。第一行是填入用于管理域 ESXi 主机的主机名,比如下图中的 sfo01-m01-esx01、sfo01-m01-esx02、sfo01-m01-esx03、sfo01-m01-esx04;第二行是填入 ESXi 主机的 IP 地址,这些 ESXi 主机需要全部配置 DNS 正反向解析,否则在部署过程将会失败。第三行和第四行用于定义 vMotion 网络和 vSAN 网络的 VMkernel 所使用的地址,在安装部署的时候会将这些地址分配给主机,通常是有多少台主机则需要配置多少个地址,起始到结束IP需要大于或等于主机的数量。

下图用于填入 ESXi 主机的安全指纹(包含 SSH 和 SSL),如果选择 Yes 则需要填写,如果选择 No 则在部署过程中不进行验证,主要用于安全目的。注意,如果要填这些信息,ESXi 主机名需要与实际对应。

关于这些信息,需要以 admin 登录到 VMware Cloud Builder 并切换到 root 用户,然后使用下面命令进行获取,以 vcf-mgmt01-esxi01.mulab.local 主机为例,你需要将下面命令中的主机地址换成你的。

  • 获取 ESXi 主机 SSH RSA Key Fingerprints(SHA256)
ssh-keygen -lf <(ssh-keyscan vcf-mgmt01-esxi01.mulab.local 2>/dev/null)
  • 获取 SSL Thumbprints(SHA256)
openssl s_client -connect vcf-mgmt01-esxi01.mulab.local:443 < /dev/null 2> /dev/null | openssl x509 -sha256 -fingerprint -noout -in /dev/stdin

3)NSX Host Overlay Network - DHCP/STATIC

下面是 NSX Host Overlay Network 参数,这块儿 NSX Overlay 网络的配置单独拎出来了。最上面配置 NSX Overlay 的 VLAN ,与其他网络类型一样。如果"Configure NSX Host Overlay Using a Static IP Pool"这个地方选择 No,则使用环境当中的 DHCP 服务器进行分配 TEP 地址;如果选择 Yes,则使用下图中所配置的创建的 TEP 地址池中的地址进行分配。注意, ESXi 上每个参与 TEP 网络的网口都需要分配地址,比如一共有 4 台主机,每台主机有 2 个网卡用于 NSX Overlay 网络,也就是前面所说的 Profile-2 和 Profile-3 ,那么总共就有 8 个网口需要 TEP 地址,配置 NSX Overlay 起始到结束 IP 就需要大于等于 8 个。

四、Deploy Parameters

最后一个 Deploy Parameters 参数表,此参数表主要用于定义 VCF 管理域相关组件的配置信息,以及为这些组件提供外部服务的 DNS 和 NTP 服务器等。一共分为五大类,分别是 Existing Infrastructure Details、License Keys、vSphere Infrastructure、NSX、SDDC Manager,下面来看看关于这些信息的详细内容。

1)Existing Infrastructure Details

配置用于 VCF 管理组件的 DNS 和 NTP 服务器,根据情况填入实际环境中的 IP 地址,请一定确保 VCF 管理组件全部在 DNS 服务器上配置了正反向域名解析,后面是 DNS 域名,确保 VCF 所有管理组件都属于同一个 DNS 域,可根据需要是否启用 CEIP(客户体验改善计划) 和 FIPS(联邦信息处理标准)安全模式。

2)License Keys

定义 VCF 管理组件的许可证密钥,选择 No 并输入 VCF BOM 清单中组件所支持的 License。注意,如果有序列号请一定要在这里提前填入所有产品的 License,如果没有填将以评估模式进行部署 VCF ,并且后续无法在 SDDC Manager 添加服役主机以及创建 VI 工作负载域。

3)vSphere Infrastructure

定义 VCF 管理组件 vSphere、vSAN 以及 VCF 环境的配置信息。在 VCF 管理域自动化部署期间,将创建一个 vCenter Server,需要定义这个 vCenter Server 的主机名(FQDN)以及 IP 地址,跟前面虚拟机(VM)管理网络相对应,并设置 vCenter Server 的部署大小,默认 Small 型即可,存储磁盘大小默认皆可。在 vCenter Server 会创建一个数据中心,数据中心下面创建一个集群,这个集群下面放置所有用于 VCF 管理域的 ESXi 主机;是否开启 vLCM 基于映像(Image)的生命周期管理方式, 如果后面配置该集群 vSAN 基于 ESA 架构,则必须选择 Yes;配置这个集群的 EVC 模式,可以根据 ESXi 主机 CPU 所支持的基准选择对应的 EVC 模式;VCF 管理域只支持使用 vSAN 作为主要存储,在 vSphere Datastore 下面定义 vSAN 存储的名称;是否开启重删和压缩,该选项仅适用于 vSAN OSA 架构,vSAN ESA 架构可以在 vSAN 虚拟机存储策略中指定;是否开启 vSAN ESA 部署架构,这里比较重要了,从该版本(5.1.0)才开始支持 vSAN ESA 部署架构作为 VCF 管理域的数据存储,如果要部署 vSAN ESA 架构,需要 ESXi 主机的磁盘是基于 NVMe 的 SSD 硬盘,同时硬盘必须满足 HCL 兼容性要求,如果你的 VMware Cloud Builder 不能联网,那需要在"Path to HCL JSON File"后面手动填入 vSAN HCL 的 JSON 文件,比如 /opt/vmware/bringup/tmp/all.json,我们需要手动下载 https://partnerweb.vmware.com/service/vsan/all.json 文件并将该文件上传到 VMware Cloud Builder 虚拟机,然后在部署之前会去验证 ESXi 主机的兼容性,如果主机不满足兼容性要求,则无法继续下一步,我这次部署将采用嵌套的 ESXi 来部署 vSAN ESA 架构的 VCF,如果你正常按上述步骤去配置,那肯定会有问题,有其他方法可以来解决这个问题,具体方法请继续关注后面文章。当然,如果 VMware Cloud Builder 不能联网也可以配置 Proxy Server 代理服务器进行联网并在线去下载 vSAN HCL 的 JSON 文件进行验证。最后配置 VCF 部署架构采用那种类型,支持标准部署和整合部署,默认情况下选择标准部署,如果选择整合部署则会自动创建下面所配置的资源池进行隔离 VCF 管理相关组件。

4)NSX

定义 NSX 组件的配置信息,默认情况下在 VCF 管理域自动部署过程中会创建一个由 3 节点 NSX Manager 所组成的 NSX 管理集群,所以需要定义这个 NSX 集群的 VIP 以及对应的主机名(FQDN),包括 3 个节点的 IP 地址以及主机名(FQDN),NSX 虚拟机的部署大小默认是 Medium 中型,可根据环境自行选择那种部署大小。后面你的环境资源有限,可能不足以部署完整的 3 节点集群,有些小技巧可以只部署 1 个 NSX Manager 节点,具体方法请继续关注后续文章。

5)SDDC Manager

定义 SDDC Manager 组件的配置信息,在 VCF 管理域自动部署过程中会创建一个 SDDC Manager 虚拟机,所以需要定义这个虚拟机的 IP 地址以及主机名(FQDN),配置管理域网络池的名称以及管理域的名称。

五、JSON 文件

使用 VMware Cloud Builder 部署 VMware Cloud Foundation 管理域,除了支持使用上面所说的 Excel 参数表,还支持使用 JSON 格式的参数文件,这种格式的参数文件可能更容易阅读以及修改。我们可以使用 Cloud Builder 中的
SOS 程序
将上面的 Excel 参数表转换为部署 VCF 所支持的 JSON 文件。

1)SSH 以 admin 用户登录到 VMware Cloud Builder 并切换到 root 用户。

su -

2)将已经配置好的 Excel 参数表通过 SFTP 上传到 Cloud Builder。

cp /home/admin/vcf-ems-deployment-parameter.xlsx /tmp

3)运行下面命令即可将 Excel 参数表转换成 JSON 格式的参数文件。

/opt/vmware/sddc-support/sos --jsongenerator --jsongenerator-input /tmp/vcf-ems-deployment-parameter.xlsx --jsongenerator-design vcf-ems-deployment-parameter

4)生成的 JSON 文件默认情况下会放到以下目录。

cd /opt/vmware/sddc-support/cloud_admin_tools/Resources/vcf-ems-deployment-parameter/

5)可以使用 SFTP 将 JSON 下载到本地以备用,最终生成的 VCF JSON 文件示例如下:

{
    "subscriptionLicensing": true,
  "skipEsxThumbprintValidation": false,
  "managementPoolName": "sfo-m01-np01",
  "sddcManagerSpec": {
    "secondUserCredentials": {
      "username": "vcf",
      "password": ""
    },
    "ipAddress": "172.16.11.59",
    "hostname": "sfo-vcf01",
    "rootUserCredentials": {
      "username": "root",
      "password": ""
    },
    "localUserPassword": ""
  },
  "sddcId": "sfo-m01",
  "esxLicense": "",
  "taskName": "workflowconfig/workflowspec-ems.json",
  "ceipEnabled": false,
  "fipsEnabled": false,
  "ntpServers": ["172.16.11.253"],
  "dnsSpec": {
    "subdomain": "sfo.rainpole.io",
    "domain": "sfo.rainpole.io",
    "nameserver": "172.16.11.4"
  },
  "networkSpecs": [
    {
      "networkType": "MANAGEMENT",
      "subnet": "172.16.11.0/24",
      "gateway": "172.16.11.253",
      "vlanId": "1611",
      "mtu": "1500",
      "portGroupKey": "sfo01-m01-cl01-vds01-pg-mgmt",
      "standbyUplinks":[],
      "activeUplinks":[
        "uplink1",
        "uplink2"
      ]
    },
    {
      "networkType": "VMOTION",
      "subnet": "172.16.12.0/24",
      "gateway": "172.16.12.253",
      "vlanId": "1612",
      "mtu": "9000",
      "portGroupKey": "sfo01-m01-cl01-vds01-pg-vmotion",
      "includeIpAddressRanges": [{"endIpAddress": "172.16.12.104", "startIpAddress": "172.16.12.101"}],
      "standbyUplinks":[],
      "activeUplinks":[
        "uplink1",
        "uplink2"
      ]
    },
    {
      "networkType": "VSAN",
      "subnet": "172.16.13.0/24",
      "gateway": "172.16.13.253",
      "vlanId": "1613",
      "mtu": "9000",
      "portGroupKey": "sfo01-m01-cl01-vds01-pg-vsan",
      "includeIpAddressRanges": [{"endIpAddress": "172.16.13.104", "startIpAddress": "172.16.13.101"}],
      "standbyUplinks":[],
      "activeUplinks":[
        "uplink1",
        "uplink2"
      ]
    },
    {
      "networkType": "VM_MANAGEMENT",
      "subnet": "172.16.15.0/24",
      "gateway": "172.16.15.253",
      "vlanId": "1610",
      "mtu": "9000",
      "portGroupKey": "sfo01-m01-cl01-vds01-pg-vm-mgmt",
      "standbyUplinks":[],
      "activeUplinks":[
        "uplink1",
        "uplink2"
      ]
    }
  ],
  "nsxtSpec":
  {
    "nsxtManagerSize": "medium",
    "nsxtManagers": [
      {
          "hostname": "sfo-m01-nsx01a",
          "ip": "172.16.11.66"
      },
      {
          "hostname": "sfo-m01-nsx01b",
          "ip": "172.16.11.67"
      },
      {
          "hostname": "sfo-m01-nsx01c",
          "ip": "172.16.11.68"
      }
    ],
    "rootNsxtManagerPassword": "",
    "nsxtAdminPassword": "",
    "nsxtAuditPassword": "",
    "vip": "172.16.11.65",
    "vipFqdn": "sfo-m01-nsx01",
    "nsxtLicense": "",
    "transportVlanId": 1614
  },
  "vsanSpec": {
      "vsanDedup": "false",
      "esaConfig": {
        "enabled": false
      },
      "datastoreName": "sfo-m01-cl01-ds-vsan01"
  },
  "dvsSpecs": [
    {
      "dvsName": "sfo-m01-cl01-vds01",
      "vmnics": [
        "vmnic0",
        "vmnic1"
      ],
      "mtu": 9000,
      "networks":[
        "MANAGEMENT",
        "VMOTION",
        "VSAN",
        "VM_MANAGEMENT"
      ],
      "niocSpecs":[
        {
          "trafficType":"VSAN",
          "value":"HIGH"
        },
        {
          "trafficType":"VMOTION",
          "value":"LOW"
        },
        {
          "trafficType":"VDP",
          "value":"LOW"
        },
        {
          "trafficType":"VIRTUALMACHINE",
          "value":"HIGH"
        },
        {
          "trafficType":"MANAGEMENT",
          "value":"NORMAL"
        },
        {
          "trafficType":"NFS",
          "value":"LOW"
        },
        {
          "trafficType":"HBR",
          "value":"LOW"
        },
        {
          "trafficType":"FAULTTOLERANCE",
          "value":"LOW"
        },
        {
          "trafficType":"ISCSI",
          "value":"LOW"
        }
      ],
      "nsxtSwitchConfig": {
        "transportZones": [ {
          "name": "sfo-m01-tz-overlay01",
          "transportType": "OVERLAY"
        },
        {
          "name": "sfo-m01-tz-vlan01",
          "transportType": "VLAN"
        }
        ]
      }
    }
  ],
  "clusterSpec":
  {
    "clusterName": "sfo-m01-cl01",
    "clusterEvcMode": "",
    "clusterImageEnabled": true,
    "vmFolders": {
      "MANAGEMENT": "sfo-m01-fd-mgmt",
      "NETWORKING": "sfo-m01-fd-nsx",
      "EDGENODES": "sfo-m01-fd-edge"
    }
  },
  "pscSpecs": [
    {
      "adminUserSsoPassword": "",
      "pscSsoSpec": {
        "ssoDomain": "vsphere.local"
      }
    }
  ],
  "vcenterSpec": {
      "vcenterIp": "172.16.11.62",
      "vcenterHostname": "sfo-m01-vc01",
      "vmSize": "small",
      "storageSize": "",
      "rootVcenterPassword": ""
  },
  "hostSpecs": [
    {
      "association": "sfo-m01-dc01",
      "ipAddressPrivate": {
        "ipAddress": "172.16.11.101"
      },
      "hostname": "sfo01-m01-esx01",
      "credentials": {
        "username": "root",
        "password": ""
      },
      "sshThumbprint": "",
      "sslThumbprint": "",
      "vSwitch": "vSwitch0"
    },
    {
      "association": "sfo-m01-dc01",
      "ipAddressPrivate": {
        "ipAddress": "172.16.11.102"
      },
      "hostname": "sfo01-m01-esx02",
      "credentials": {
        "username": "root",
        "password": ""
      },
      "sshThumbprint": "",
      "sslThumbprint": "",
      "vSwitch": "vSwitch0"
    },
    {
      "association": "sfo-m01-dc01",
      "ipAddressPrivate": {
        "ipAddress": "172.16.11.103"
      },
      "hostname": "sfo01-m01-esx03",
      "credentials": {
        "username": "root",
        "password": ""
      },
      "sshThumbprint": "",
      "sslThumbprint": "",
      "vSwitch": "vSwitch0"
    },
    {
      "association": "sfo-m01-dc01",
      "ipAddressPrivate": {
        "ipAddress": "172.16.11.104"
      },
      "hostname": "sfo01-m01-esx04",
      "credentials": {
        "username": "root",
        "password": ""
      },
      "sshThumbprint": "",
      "sslThumbprint": "",
      "vSwitch": "vSwitch0"
    }
  ]
}

其实,还可以使用由 VMware 工程师 Martin 个人所创建的在线 VCF JSON 文件生成器网页(
https://martingustafsson.com/vcf-ui-json/
)来制作用于 VCF 的 JSON 文件,如下图所示。

大文件上传是前端开发中常见的需求之一,特别是在需要处理高清图片、视频或其他大型文件时。优化大文件上传不仅可以提升用户体验,还能有效减轻服务器负担。本文将深入探讨大文件上传的几种常见优化技术,包括文件切片与并发上传、断点续传、后台处理优化、安全性考虑和用户体验优化。

1. 前言

在现代Web应用中,用户上传大文件已成为常见需求。然而,直接上传大文件会面临诸多挑战,例如网络不稳定导致上传中断、长时间上传导致用户体验差、服务器压力大等。因此,优化大文件上传性能显得尤为重要。

2. 文件切片与并发上传

2.1 文件切片原理

文件切片(Chunking)是将大文件分成若干小片段,每个片段独立上传的方法。这样做可以有效减少单次上传的数据量,降低上传失败的概率。

2.2 实现步骤

  1. 前端切片
    :利用
    Blob
    对象的
    slice
    方法将文件切片。
  2. 并发上传
    :使用
    Promise.all
    实现多个切片并发上传。
  3. 合并请求
    :上传完成后,通知服务器合并这些切片。

3. 断点续传

断点续传(Resumable Uploads)可以在上传过程中断时,从断点继续上传,避免重新上传整个文件。

3.1 实现步骤

  1. 前端记录进度
    :使用
    localStorage
    记录已上传的切片信息。
  2. 断点续传
    :上传时检查哪些切片未上传,继续上传未完成的部分。

4. 后台处理优化

4.1 分片接收与合并

服务器需要支持接收分片请求,并在所有分片上传完成后合并文件。可以利用中间件或服务端程序语言实现这一逻辑。

5. 安全性考虑

5.1 文件类型校验

在前端和后端都应对文件类型进行校验,确保上传的文件类型符合预期。

5.2 文件大小限制

限制单个文件和总上传文件的大小,防止恶意用户上传过大的文件造成服务器压力。

6. 用户体验优化

6.1 进度显示

通过显示上传进度条,让用户了解上传进度,提升用户体验。

6.2 网络波动处理

考虑到用户可能在网络不稳定的环境中上传文件,可以增加失败重试机制。

完整实例

后端代码(Node.js + Express)

安装依赖

npm init -y
npm install express multer fs

创建服务器文件(server.js)

const express = require('express');const multer = require('multer');const fs = require('fs');const path = require('path');const bodyParser = require('body-parser');const app = express();const upload = multer({ dest: 'uploads/' });app.use(bodyParser.json());// 路由:处理文件切片上传
app.post(
'/upload', upload.single('chunk'), (req, res) => {
const { index, fileName } = req.body
; const chunkPath = path.join(__dirname, 'uploads', `${fileName}-${index}`); fs.renameSync(req.file.path, chunkPath); res.status(200).send('Chunk uploaded');});// 路由:合并切片
app.post(
'/merge', (req, res) => {
const { totalChunks, fileName } = req.body
; const filePath = path.join(__dirname, 'uploads', fileName); const writeStream = fs.createWriteStream(filePath);for (let i =0;i < totalChunks; i++) { const chunkPath = path.join(__dirname, 'uploads', `${fileName}-${i}`); const data = fs.readFileSync(chunkPath); writeStream.write(data); fs.unlinkSync(chunkPath);}

writeStream.end()
; res.status(200).send('File merged');});app.listen(3000, () => {
console.log(
'Server started on http://localhost:3000');});

前端代码(index.html + script.js)

  1. 创建HTML文件(index.html)
<!DOCTYPE html>
<html lang=
"en">
<head>
<meta charset=
"UTF-8">
<meta name=
"viewport" content="width=device-width, initial-scale=1.0">
<title>大文件上传</title>
</head>
<body>
<input type=
"file" id="fileInput">
<progress id=
"progressBar" value="0" max="100"></progress>
<button onclick=
"uploadFile()">上传文件</button>
<script src=
"script.js"></script>
</body>
</html>

  1. 创建JavaScript文件(script.js)
const fileInput = document.getElementById('fileInput');const progressBar = document.getElementById('progressBar');const chunkSize = 5 * 1024 * 1024;// 5MB
const uploadChunk = async (chunk, index, fileName) => {
const formData = new FormData()
; formData.append('chunk', chunk); formData.append('index', index); formData.append('fileName', fileName);await fetch('/upload', {method: 'POST',body:formData
})
;updateProgressBar(index);};const updateProgressBar = (index) => {
const uploadedChunks = JSON.parse(localStorage.getItem(
'uploadedChunks')) || [];if (!uploadedChunks.includes(index)) {
uploadedChunks.
push(index); progressBar.value = (uploadedChunks.length / totalChunks) * 100; localStorage.setItem('uploadedChunks', JSON.stringify(uploadedChunks));}
}
;const uploadFile = async () => {
const file = fileInput.files[
0]; const totalChunks = Math.ceil(file.size / chunkSize); const uploadedChunks = JSON.parse(localStorage.getItem('uploadedChunks')) || []; const promises = [];for (let i =0;i < totalChunks; i++) { if (!uploadedChunks.includes(i)) {
const chunk = file.slice(i * chunkSize, (i +
1) * chunkSize); promises.push(uploadChunk(chunk, i, file.name));}
}

await Promise.all(promises)
;await fetch('/merge', {method: 'POST',headers: { 'Content-Type': 'application/json'},body: JSON.stringify({ totalChunks, fileName:file.name })
})
;localStorage.removeItem('uploadedChunks'); alert('文件上传成功');};

启动后端服务器

  1. 在浏览器中打开前端页面


index.html
文件在浏览器中打开,选择文件并点击“上传文件”按钮即可看到文件上传进度。

node server.js

写在前面

本随笔是非常菜的菜鸡写的。如有问题请及时提出。

可以联系:1160712160@qq.com

GitHhub:
https://github.com/WindDevil
(目前啥也没有

动机

由于WPS的提取图片需要收费,作为穷鬼的我直接在
bing
,键入
open source software get picture form pdf
.

找到这个项目:
VR51/Batch-PDF-Image-Extractor: Extract images from PDF documents. Works on multiple and single PDF files (github.com)

看到这个项目的
Requirements
:
This script reqires pdfimages to be installed. The script will check for pdfimages and prompt for its installation if not found.

显示需要
pdfimages
这个工具.

安装

于是
继续搜索
pdfimages
,得到
这个网站
.

我们可以看到这个库可以直接通过
apt-get
得到.这里由于需要用到Linux,我使用的是虚拟机的方法可以参考
这篇博客
.

于是在虚拟机里键入
sudo apt-get install poppler-utils
,注意它的名字是
poppler-utils
.

使用方法

直接键入
pdfimages -h
尝试查找它的教程:

pdfimages version 22.02.0
Copyright 2005-2022 The Poppler Developers - http://poppler.freedesktop.org
Copyright 1996-2011 Glyph & Cog, LLC
Usage: pdfimages [options] <PDF-file> <image-root>
  -f <int>       : first page to convert
  -l <int>       : last page to convert
  -png           : change the default output format to PNG
  -tiff          : change the default output format to TIFF
  -j             : write JPEG images as JPEG files
  -jp2           : write JPEG2000 images as JP2 files
  -jbig2         : write JBIG2 images as JBIG2 files
  -ccitt         : write CCITT images as CCITT files
  -all           : equivalent to -png -tiff -j -jp2 -jbig2 -ccitt
  -list          : print list of images instead of saving
  -opw <string>  : owner password (for encrypted files)
  -upw <string>  : user password (for encrypted files)
  -p             : include page numbers in output file names
  -q             : don't print any messages or errors
  -v             : print copyright and version info
  -h             : print usage information
  -help          : print usage information
  --help         : print usage information
  -?             : print usage information

很容易看出使用方法是
pdfimages [options] <PDF-file> <image-root>
,也即
pdfimages+选项+PDF文件路径+输出图片路径
.

这里比较重点地点出,如果直接使用指令,默认生成格式不是
jpg
或者
png
,需要在输入指令的时候选项部分输入
-png
或者
-j
,这个指令就可以把
pdffile.pdf
的图片提取到当前目录下:

pdfimages -j pdffile.pdf ./

实验


Downloads
文件夹下创建
files
,把主机PDF文件拷贝到虚拟机.

cd ~/Downloads
mkdir files
cd files

我直接用VScode的SSH把文件拖进去的.

写在前面

本随笔是非常菜的菜鸡写的。如有问题请及时提出。

可以联系:1160712160@qq.com

GitHhub:
https://github.com/WindDevil
(目前啥也没有

动机

由于WPS的提取图片需要收费,作为穷鬼的我直接在
bing
,键入
open source software get picture form pdf
.

找到这个项目:
VR51/Batch-PDF-Image-Extractor: Extract images from PDF documents. Works on multiple and single PDF files (github.com)

看到这个项目的
Requirements
:
This script reqires pdfimages to be installed. The script will check for pdfimages and prompt for its installation if not found.

显示需要
pdfimages
这个工具.

安装

于是
继续搜索
pdfimages
,得到
这个网站
.

我们可以看到这个库可以直接通过
apt-get
得到.这里由于需要用到Linux,我使用的是虚拟机的方法可以参考
这篇博客
.

于是在虚拟机里键入
sudo apt-get install poppler-utils
,注意它的名字是
poppler-utils
.

使用方法

直接键入
pdfimages -h
尝试查找它的教程:

pdfimages version 22.02.0
Copyright 2005-2022 The Poppler Developers - http://poppler.freedesktop.org
Copyright 1996-2011 Glyph & Cog, LLC
Usage: pdfimages [options] <PDF-file> <image-root>
  -f <int>       : first page to convert
  -l <int>       : last page to convert
  -png           : change the default output format to PNG
  -tiff          : change the default output format to TIFF
  -j             : write JPEG images as JPEG files
  -jp2           : write JPEG2000 images as JP2 files
  -jbig2         : write JBIG2 images as JBIG2 files
  -ccitt         : write CCITT images as CCITT files
  -all           : equivalent to -png -tiff -j -jp2 -jbig2 -ccitt
  -list          : print list of images instead of saving
  -opw <string>  : owner password (for encrypted files)
  -upw <string>  : user password (for encrypted files)
  -p             : include page numbers in output file names
  -q             : don't print any messages or errors
  -v             : print copyright and version info
  -h             : print usage information
  -help          : print usage information
  --help         : print usage information
  -?             : print usage information

很容易看出使用方法是
pdfimages [options] <PDF-file> <image-root>
,也即
pdfimages+选项+PDF文件路径+输出图片路径
.

这里比较重点地点出,如果直接使用指令,默认生成格式不是
jpg
或者
png
,需要在输入指令的时候选项部分输入
-png
或者
-j
,这个指令就可以把
pdffile.pdf
的图片提取到当前目录下:

pdfimages -j pdffile.pdf ./

实验


Downloads
文件夹下创建
files
,把主机PDF文件拷贝到虚拟机.

cd ~/Downloads
mkdir files
cd files

我直接用
VScode的SSH
把文件拖进去的.

然后执行
pdfimages -j LDO.pdf ./
,使用
ls
指令看一下生成了什么:

-000.ppm  -001.ppm  -002.ppm  -003.ppm  -004.ppm  -005.ppm  LDO.pdf

很奇怪地没用生成
.jpg
而是
.ppm
,挠头.

改为执行
pdfimages -png LDO.pdf ./
,使用
ls
指令看一下生成了什么:

-000.png  -000.ppm  -001.png  -001.ppm  -002.png  -002.ppm  -003.png  -003.ppm  -004.png  -004.ppm  -005.png  -005.ppm  LDO.pdf

确实可以生成
.png
文件.

然后只需要把图片从虚拟机拷出来就行了.

然后执行
pdfimages -j LDO.pdf ./
,使用
ls
指令看一下生成了什么:

-000.ppm  -001.ppm  -002.ppm  -003.ppm  -004.ppm  -005.ppm  LDO.pdf

很奇怪地没用生成
.jpg
而是
.ppm
,挠头.

改为执行
pdfimages -png LDO.pdf ./
,使用
ls
指令看一下生成了什么:

-000.png  -000.ppm  -001.png  -001.ppm  -002.png  -002.ppm  -003.png  -003.ppm  -004.png  -004.ppm  -005.png  -005.ppm  LDO.pdf

确实可以生成
.png
文件.

然后只需要把图片从虚拟机拷出来就行了.

一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的进程 --《庄子·秋水》

好吧,现在是2024年7月24日,我现在正坐在某编程机构的办公室电脑旁,写下这些文字,是啊,我已经退役将近两年了,而这两年里,我的人生已经发生了翻天覆地的变化,发生了很多事,我对OI的看法也一而再的转变。而我只是历史洪流里的一小粒沙子罢了,可能我的故事没有人会听,没有人会看一个中强省强市弱校的再普通不过的失败者,但是我还是想把故事写下来,记录下那些只属于我的经历。

故事要从小学说起,我并没有去一个厉害的小学,小学的时候,我自然不是什么老师家长口中的“好学生”,学习成绩虽然不错,但整天是正事不干,唯独对计算机感兴趣。当时我自然是不知道C++和信息学奥赛之类的,也看不懂那些英文符号,但是当时我了解了一种中文编程语言--E语言。当时我用E语言编写了各种窗口小工具,还总想着做一些整人的“病毒”,比如锁机程序,假的刷q币工具之类的,当然从未真正给别人用。但是到五年级的时候,我开始沉迷于各种游戏,主要是王者荣耀和MineCraft,然后自然就是和家长的各种斗争。所以我小学基本是每天遭受老师和家长的各种批评和训斥。

上初中之后,初一的时候,一切都看起来很美好,初一上的时候,还参加了很多活动,一切都是那么欣欣向荣,但是,实际上,正常的中学生活早已走到尽头,那时候,学校还发过一个学科奥赛的册子(实际上是区里发的),上面写着某某竞赛拿到奖自招降分了之类的里面就提到了OI。当然,我们初中只是区里相对较差的初中:青岛西海岸新区外国语学校,绝大多数人都不知道学科竞赛是啥,自然也没有人关注这个册子,包括我,我也忘记了册子的细节。初一下的时候,《流浪地球》电影上映,看完之后(ps:这部电影超好看,我看了好多遍,比2好看多了!!),我燃起了学习正儿八经的编程语言的兴趣,当然

谁又能想的到这一年来发生的变化,谁又能想的到最终会以这样的一种结局退役呢?这一年来,我对做oi题的热情突然下降,人也就像一下子失去斗志一样,也就开始摆烂了。NOIP2022之前,其实我就已经预料到了我不会有什么好结局,再也不像初三或者高一时候的热情了,或者说高二上最开始有着热情,那时我已经基本上看完了一本通,学习了很多数据结构,学会了生成函数,于是就想着给自己制定了很庞大的计划学习知识点,但是这时候我还没有意识到我oi生涯中犯得一个最致命的错误,后来把这些热情全都用在与停课做斗争了,当时为了停课,可谓是往死里拼,每天都要和家长大吵一架,家长一直没同意, 老师也不支持,学校领导更别提了,更难过的是,连我的教练

人最重要的还是为自己,不要总觉得自己亏欠着什么,要拥有自信,要沉下心思考,不要过度追求严谨性,不要过度追求证明,一定要多写代码,不要害怕调试,不要害怕细节处理,害怕调试的主要原因是时间不够,有机会一定要多出去集训而不是自学,文化课辅导班没有用处,千万不能对互联网产生抵触,这样只会更难利用。做不下去题开始摆烂打游戏是正常的,说明已经很累了,谁也做不到一整天12小时都在做题,但是 累了打游戏越来越累,要赶紧起来休息。一定不要死磕,要多看答案,多看题解,所谓“独立思考”本身就是不成立的,不要想着像那些真正的大佬一样自己发明什么东西或者证明什么东西。要学就要深入去学,不要听信所谓“全面发展”,啥都只会一点。

我们只不过是一台自动驾驶的汽车罢了,哪里有所谓的自由意志,当面临选择时,细胞中的某些电子随机选择一个方向坍缩,然后放大表现出来罢了。人实际上不可能主导自己的命运,而是物理过程主导着我们,即使是量子力学也不能改变这一事实,因为量子力学的随机性,只不过是超出我们宇宙之外的不可知的一串序列罢了,这就是命运。也许你看到了我的故事,决定努力改变自己的现状,也许,你最后成功了,但是,这只是宇宙选择了你,虽然并不是大爆炸开始就确定好的,但是,你我也无从改变,或许,你也失败了,但是,请不要伤心,因为,一切,都已经是注定好了的,你我都只是来感受这一切的。可是,这又有什么用呢?该伤心的人还会伤心,因为,我无法控制你大脑中神经元的放电,无法控制量子的坍缩,而这,都是命运的一部分,不可避免的一部分。