2023年3月

PowerPoint是用于制作幻灯片(演示文稿)的应用软件,每张幻灯片中都可以包含文字、图形、图形、表格、声音和影像等多种信息。有时候我们发现在PPT里面有一些精美的图片,或者其他原因想要把PPT里面的图片保存下来。但如果PowerPoint文档中包含大量图片,一张张保存未免太费时间及精力。那有什么办法可以高效便捷地提取出PPT中的图片呢?在这篇文章中,您将学习如何以编程方式从PowerPoint文档中提取图片。下面是我整理的步骤及方法,并附上C#/VB.NET代码供大家参考。

  • 从整个演示文稿中提取图像
  • 从特定演示幻灯片中提取图像

程序环境:

本次测试时,在程序中引入 Free Spire.Presentation.dll 文件。

方法
1


​Free Spire.Presentation for .NET​​
下载到本地,解压,找到 BIN 文件夹下的 Spire.Presentation.dll。然后在 Visual Studio 中打开“解决方案资源管理器”,鼠标右键点击“引用”,“添加引用”,将本地路径 BIN 文件夹下的 dll 文件添加引用至程序。

方法
2

:

通过 ​
NuGet
​​安装。可通过以下 2 种方法安装:

1. 可以在 Visual Studio 中打开“解决方案资源管理器”,鼠标右键点击“引用”,“管理 NuGet 包”,然后搜索“Free Spire.Presentation”,点击“安装”。等待程序安装完成。

2. 将以下内容复制到 PM 控制台安装。

Install-Package FreeSpire.Presentation -Version 7.8

从整个演示文稿中提取图像

  • 初始化 Presentation 类的一个实例。
  • 使用 Presentation.LoadFromFile() 方法加载 PowerPoint 演示文稿。
  • 通过 Presentation.Images 属性获取演示文稿中所有图片的集合。
  • 遍历集合,调用ImageCollection[int].Image.Save()方法将集合中的图片保存到图片文件中。

完整代码

C#

usingSpire.Presentation;usingSpire.Presentation.Collections;usingSystem.Drawing;namespaceExtractImagesFromPresentation
{
internal classProgram
{
static void Main(string[] args)
{
//初始化Presentation类的实例 Presentation ppt = newPresentation();//加载PowerPoint演示文稿 ppt.LoadFromFile("示例文档.pptx");//获取演示文稿的图像集 ImageCollection imageCollection =ppt.Images;//遍历集合中的图像 for (int i = 0; i < imageCollection.Count; i++)
{
//提取图像 imageCollection[i].Image.Save(string.Format("Presentation\\图片{0}.png", i));
}

ppt.Dispose();
}
}
}

VB.NET

ImportsSpire.PresentationImportsSpire.Presentation.CollectionsNamespaceExtractImagesFromPresentationFriend ClassProgramPrivate Shared Sub Main(ByVal args As String())'初始化Presentation类的实例
            Dim ppt As Presentation = NewPresentation()'加载PowerPoint演示文稿
            ppt.LoadFromFile("示例文档.pptx")'获取演示文稿的图像集
            Dim imageCollection As ImageCollection =ppt.Images'遍历集合中的图像
            For i As Integer = 0 To imageCollection.Count - 1
                '提取图像
                imageCollection(i).Image.Save(String.Format("Presentation\图片{0}.png", i))Nextppt.Dispose()End Sub
    End Class
End Namespace

效果图

从特定演示幻灯片中提取图像

  • 初始化 Presentation 类的一个实例。
  • 使用 Presentation.LoadFromFile() 方法加载 PowerPoint 演示文稿。
  • 通过 Presentation.Slides[int] 属性按索引获取特定幻灯片。
  • 遍历幻灯片上的所有形状。
  • 检查形状是否为 SlidePicture 或 PictureShape 类型。 如果结果为真,则使用 SlidePicture.PictureFill.Picture.EmbedImage.Image.Save()或 PictureShape.EmbedImage.Image.Save() 方法将图像保存到图像文件。

完整代码

C#

usingSpire.Presentation;namespaceExtractImagesFromSlide
{
internal classProgram
{
static void Main(string[] args)
{
//初始化 Presentation 类的一个实例 Presentation ppt = newPresentation();//加载 PowerPoint 演示文稿 ppt.LoadFromFile("示例文档.pptx");//获取指定幻灯片 ISlide slide = ppt.Slides[1];int i = 0;//遍历指定幻灯片上的所有形状 foreach (IShape s inslide.Shapes)
{
//检查形状是否为SlidePicture类型 if (s isSlidePicture)
{
//提取图像 SlidePicture ps = s asSlidePicture;
ps.PictureFill.Picture.EmbedImage.Image.Save(
string.Format(@"D:\.NET\提取图片\bin\Debug\Slide\图像{0}.png", i));
i
++;
}
//检查形状是否为 PictureShape 类型 if (s isPictureShape)
{
//提取图像 PictureShape ps = s asPictureShape;
ps.EmbedImage.Image.Save(
string.Format(@"D:\.NET\提取图片\bin\Debug\Slide\图像{0}.png", i));
i
++;
}
}
}
}
}

VB.NET

ImportsSpire.PresentationNamespaceExtractImagesFromSlideFriend ClassProgramPrivate Shared Sub Main(ByVal args As String())'初始化 Presentation 类的一个实例
            Dim ppt As Presentation = NewPresentation()'加载 PowerPoint 演示文稿
            ppt.LoadFromFile("示例文档.pptx")'获取指定幻灯片
            Dim slide As ISlide = ppt.Slides(1)Dim i = 0
            '遍历指定幻灯片上的所有形状
            For Each s As IShape Inslide.Shapes'检查形状是否为SlidePicture类型
                If TypeOf s Is SlidePicture Then
                    '提取图像
                    Dim ps As SlidePicture =TryCast(s, SlidePicture)
ps.PictureFill.Picture.EmbedImage.Image.Save(
String.Format("D:\.NET\提取图片\bin\Debug\Slide\图像{0}.png", i))
i
+= 1 End If '检查形状是否为 PictureShape 类型 If TypeOf s Is PictureShape Then '提取图像 Dim ps As PictureShape =TryCast(s, PictureShape)
ps.EmbedImage.Image.Save(
String.Format("D:\.NET\提取图片\bin\Debug\Slide\图像{0}.png", i))
i
+= 1 End If Next End Sub End Class End Namespace

效果图

—本文完—

简介

我们在flutter中可以使用Navigator.push或者Navigator.pushNamed方法来向Navigator中添加不同的页面,从而达到页面调整的目的。

一般情况下这样已经足够了,但是有时候我们有多个Navigator的情况下,上面的使用方式就不够用了。比如我们有一个主页面app的Navigator,然后里面有一个匹配好友的功能,这个功能有多个页面,因为匹配好友功能的多个页面实际上是一个完整的流程,所以这些页面需要被放在一个子Navigator中,并和主Navigator区分开。

那么应该如何处理呢?

搭建主Navigator

主Navigator是我们app的一些主要界面,这里我们有三个界面,分别是主home界面,一个setting配置界面和好友匹配界面。

其中好友匹配界面包含了三个子界面,这三个子界面将会用到子路由。

先来看下主路由,主路由的情况跟普通的路由没啥区别,这里我们首先定义和home和setting匹配的两个widget:HomePage和SettingsPage:

class HomePage extends StatelessWidget {
  const HomePage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(context),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 24.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: const [
              SizedBox(
                width: 250,
                height: 250,
                child: Center(
                  child: Icon(
                    Icons.home,
                    size: 175,
                    color: Colors.blue,
                  ),
                ),
              ),
              SizedBox(height: 32),
              Text(
                '跳转到好友匹配页面',
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).pushNamed(routeFriendMatch);
        },
        child: const Icon(Icons.add),
      ),
    );
  }

HomePage很简单,它包含了一个floatingActionButton,当点击它的时候会调用 Navigator.pushNamed方法进行路由切换。

然后是SettingsPage,它是一个简单的Column:

class SettingsPage extends StatelessWidget {
  const SettingsPage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(),
      body: SingleChildScrollView(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: List.generate(8, (index) {
            return  ListTile(
              title: Text('设置项$index'),
            );
          }),
        ),
      ),
    );
  }

最后一个页面是FriendMatchFlow,这个页面比较复杂,我们在下一个再进行讲解。

然后我们为主路由在onGenerateRoute方法中进行定义:

void main() {
  runApp(
    MaterialApp(
      onGenerateRoute: (settings) {
        late Widget page;
        if (settings.name == routeHome) {
          page = const HomePage();
        } else if (settings.name == routeSettings) {
          page = const SettingsPage();
        } else if (settings.name == routeFriendMatch) {
          page = const FriendMatchFlow(
            setupPageRoute: routeFriendMatchPage,
          );
        }

        return MaterialPageRoute<dynamic>(
          builder: (context) {
            return page;
          },
          settings: settings,
        );
      },
      debugShowCheckedModeBanner: false,
    ),
  );
}

主路由很简单,跟普通的路由没有太多的区别。

构建子路由

接下来是构建子路由的步骤。在主路由中,如果路由的名称是routeFriendMatch,那么就会跳转到FriendMatchFlow。

而这个flow页面实际上是由几个子页面组成的:选择好友页面,等待页面,匹配页面和匹配完毕页面。

具体的页面代码这里就不写了,我们主要来讲一下子路由的使用。

对于FriendMatchFlow来说,它本身是一个Navigator,所以我们的build方法是这样的:

  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: _isExitDesired,
      child: Scaffold(
        appBar: _buildFlowAppBar(),
        body: Navigator(
          key: _navigatorKey,
          initialRoute: widget.setupPageRoute,
          onGenerateRoute: _onGenerateRoute,
        ),
      ),
    );
  }

因为他需要根据用户的不同点击来进行内部路由的切换,所以需要保存对当前子Navigator的应用,所以这里FriendMatchFlow是一个StatefulWidget,并且上面的_navigatorKey是一个GlobalKey对象,以提供对子Navigator的引用:

  final _navigatorKey = GlobalKey<NavigatorState>();

这里的_onGenerateRoute方法,跟主路由也是很类似的,主要定义的是子路由中页面的跳转:

  Route _onGenerateRoute(RouteSettings settings) {
    late Widget page;
    switch (settings.name) {
      case routeFriendMatchPage:
        page = WaitingPage(
          message: '匹配附近的好友...',
          onWaitComplete: _onDiscoveryComplete,
        );
        break;
      case routeFriendSelectPage:
        page = SelectFriendPage(
          onFriendSelected: _onFriendSelected,
        );
        break;
      case routeFriendConnectingPage:
        page = WaitingPage(
          message: '匹配中...',
          onWaitComplete: _onConnectionEstablished,
        );
        break;
      case routeFriendFinishedPage:
        page = FinishedPage(
          onFinishPressed: _exitSetup,
        );
        break;
    }

这里的on***Selected是VoidCallback回调,用来进行路由的切换:

  void _onDiscoveryComplete() {
    _navigatorKey.currentState!.pushNamed(routeFriendSelectPage);
  }

  void _onFriendSelected(String deviceId) {
    _navigatorKey.currentState!.pushNamed(routeFriendConnectingPage);
  }

  void _onConnectionEstablished() {
    _navigatorKey.currentState!.pushNamed(routeFriendFinishedPage);
  }

可以看到上面的路由切换实际上是在子路由上切换,跟父路由无关。

如果想要直接从子路由跳出到父路由该怎么处理呢?很简单,调用Navigator.of的pop方法即可:

  void _exitSetup() {
    Navigator.of(context).pop();
  }

这里的context默认是全局的context,所以会导致主路由的跳转变化。

总结

以上的代码运行结果如下:

虽然上面的例子看起来复杂,但是大家只要记住了不同的路由使用不同的Navigator范围进行跳转就行了。

本文的例子:
https://github.com/ddean2009/learn-flutter.git

IDEA: 如何导入项目模块 以及 将 Java程序打包 JAR 详细步骤


在这里插入图片描述

在这里插入图片描述
@

IDEA 导入项目模块 Module

一. 创建一个空项目

想要导入模块 Module ,我们需要先创建一个项目,因为 Module模块在 IDEA 中是存在于项目下的。

这里我们先创建一个空项目,当然已经有项目了,导入 Module 也是一样的,这里我们为了更好的演示效果。所有创建一个空的项目。如下图所示:

在这里插入图片描述

在这里插入图片描述

二. 导入 Module

我们先找到我们该项目
这里是Code
在我们本地电脑上磁盘中存储的位置:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三. 将 Module 与 当前项目关联上

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上述三个步骤,就将我们需要的 Module 导入到项目中了,一个项目中可以导入 多个 Module






当然其实我们是可以直接打开一个模块的,如下图所示

在这里插入图片描述

IDEA 将 Java程序打包成 JAR

在这里插入图片描述

一. 选择要打包的 Module

点击File,再点Project Structure。——> 点击Artifaces

在这里插入图片描述

在这里插入图片描述

说明:
Main Class:点击红框最后的那个小图标,项目中是哪个main方法所在的类去启动项目,这里就选择哪个类。我的项目是由app里面的main方法启动的,所以我就选择App

在这里插入图片描述

在这里插入图片描述

说明: 设置
META-INF/MANIFEST.MF
。点击箭头指向的图标进行设置。这里我就不设置的了,默认就可以了

在这里插入图片描述

说明:
如果进行了上述操作时,出现了错误提示。表示当前模块下已存在
META-INF
文件夹了。可以在本地找到已存在的META-INF文件夹,右键点击
DELETE
删除即可。查找方式如下: 右键模块点击
open in
点击
Explorer

在模块里面找到META-INF文件删除即可。

在这里插入图片描述

如果没有报错,就出现一个
puzzlegame:jar
的提示。点击右下角的
OK

在这里插入图片描述

二. Build 创建 Jar 资源包

在这里插入图片描述

点击后,弹出如下对话框

在这里插入图片描述

在这里插入图片描述

三. 找到我们打包好的 jar 资源包

点击
File
,再点
Project Structure
。点击
Artifaces
。在下图红色框中的路径下,可以找到生成好的
jar
包。

在这里插入图片描述

在这里插入图片描述






最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!!!

在这里插入图片描述

前言说明

Linux下安装MongoDB副本集我基本上是一次搭建,几百年不再碰,也记不住具体的命令,偶尔需要搭建都是直接网上找的教程。

有些教程很精简,有些又版本不一样,所以索性我整合下别人的教程,把linux下搭建副本集的步骤记录下并分享出来,希望能够帮到你们。

参考以下文章:https://developer.aliyun.com/article/983777

https://developer.aliyun.com/article/485807  -- 这个还有分片相关的东西,

安装环境

  • 单台CENT OS 7.6(腾讯云服务器的)
  • MongoDB 5.0.15
  • MobaXterm 终端工具

预期实现效果

在单台服务器上,通过配置3个不同端口,启动3个MongoDB服务来实现副本集配置。

具体实现步骤

步骤一: 下载、安装MongoDB、配置环境变量

1.1
下载

到官网下载(下图标注1)或复制下载地址(下图标注2),
https://www.mongodb.com/try/download/community

1,2标注说明:
1 是直接下载tgz包,这是用于直接文件传输到linux上,因为通过wget去下载有时候网速会很慢,所以这里下文会提到download的下载方式。

2 是复制下载链接,这是用于wget方式下载,如下文执行命令。

1.2
解压安装

通过MobaXterm连接到的你Linux主机,当然你用其他的终端工具都可以。然后进入到指定目录下,进行安装。

整体命令如下(是逐行执行,不是整体执行)
# 进入指定位置下载安装包 你也可以选择其他指定路径,确保自己清楚即可。

# 进入指定位置下载安装包  你也可以选择其他指定路径,确保自己清楚即可。 还有,下面的执行命令是一行行执行,而不是一起执行。 
cd /usr/local

# 下载安装包 注意:这里可能遇到下载速度很慢的情况,如果遇到了,直接ctrl+c取消,再按照步骤1.1的截图的1标注,直接下载安装包到本地,再通过mobaXterm文件传输工具传进去(下面有截图参考)。 因为wget执行了,/usr/local路径下有个mongodb安装包文件,先删了再传。
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-5.0.2.tgz
# 注意,执行下面命令前确保安装包是下载好了的到你指定路径。
# 解压安装包
tar -zxvf mongodb-linux-x86_64-rhel70-5.0.2.tgz

# 改名字
mv mongodb-linux-x86_64-rhel70-5.0.2mongodb

# 此时可以删除安装包 --建议先留着,等最后再删 可以不执行它
rm -rf mongodb-linux-x86_64-rhel70-5.0.2.tgz

MobaXterm上传安装包的截图

解压好安装包之后, cd 进入mongodb 再 ll 查看,看到 bin文件夹和其他几个文件说明解压成功。如下图。

1.3 配置环境变量

# 配置环境变量
vim /etc/profile

// 进入到profile之后, 添加的内容如下 注意/usr/local/mongodb是刚才安装mongodb它所在的路径,如果你不是安装在这个路径下,这里就要换成你的安装路径。 具体这两个配置的插入位置见下图
export MONGO_HOME=/usr/local/mongodb
export PATH=$PATH:$MONGO_HOME/bin

补充:vim /etc/profile 之后, 按 i 会出来 ------insert----- 此时进入编辑模式,才可以去修改。
修改好了,按住 esc键,然后 输入 :wq 保存退出。

退出后,执行以下命令让环境变量生效

source /etc/profile

步骤二:创建每个(共3个实例)MongoDB的数据、日志存储文件夹

2.1 创建数据、日志、配置存储文件夹

分别给每台服务器创建data 、 logs 、 conf文件夹,也就是一共9个,放在统一路径,方便查询管理。

# 这里直接放在根目录的data文件夹下了,你也可以选择存储到usr/local/mongodb里面。 {1,2,3}表示分别创建 mongo1 mongo2 mongo3 
mkdir -p /data/mongo{1,2,3}/data
mkdir -p /data/mongo{1,2,3}/logs
mkdir -p /data/mongo{1,2,3}/conf

执行完毕之后,可以进入到路径中看看,分别有mongo1/2/3,每一个文件夹下都有conf / data / logs 文件夹,分别用来存放配置、数据、日志。

步骤三:配置MongoDB配置文件

对每个MongoDB服务,配置服务器配置,包括IP端口、开放IP,后台运行以及主要的副本集配置。

分别执行如下命令,创建三个配置文件,把下边的配置内容粘贴进去,再保存退出

# 每一个conf 对应一个服务器, 里面配置的内容主要是端口不一样
vim /data/mongo1/conf/mongodb.conf
# 第二个服务器配置
vim /data/mongo2/conf/mongodb.conf
# 第三个服务器配置
vim /data/mongo3/conf/mongodb.conf

配置内容

port=27017 #端口 注意,三台服务器端口不一样,分别设置为27017,27018,27019同时确保Linux端口放开
bind_ip=0.0.0.0 # 默认是127.0.0.1,要想外网接入,这里就要设置为0.0.0.0# 三台服务器的数据、日志存放地址不一样,要注意。
dbpath=/data/mongo1/data #数据库存放
logpath=/data/mongo1/logs/mongodb.log #日志文件
fork=true#设置后台运行
#auth=true#开启认证
## 下面的就是副本集相关的配置,三个服务器要使用一样的副本集名称
replSet =myrs # 定义副本集名称
oplogSize = 1024 # 定义opLog大小,单位是MB,也可以设置大一些比如2048,我这里演示用,设置小点。

分别改下port  dbpath/logpath的路径,其他保持一致。 一共操作三次vim。 保存配置的时候,先检查下有没有漏字母,尤其是 port。

步骤四:分别启动三个服务

先进入到mongodb bin 路径下

# 如果你的安装路径是其他的,那就以你的为准,反正就是进入到bin路径下去执行启动服务器命令
cd /usr/local/mongodb/bin

然后依次执行启动命令

# 启动第一台
./mongod --config /data/mongo1/conf/mongodb.conf
#启动第二台
./mongod --config /data/mongo2/conf/mongodb.conf
#启动第三台
./mongod --config /data/mongo3/conf/mongodb.conf

每执行完一次,成功的话,提示如下

这时候三台服务器都启动好了,连入等下要设置为主节点的服务器,配置副本集并让其生效。这里我用27017端口的那台作为主节点。

# 进入到mongodb bin目录下,执行以下命令接入mongo
mongo --port 27017说明,端口是你准备要设置为主节点的那台服务器的端口,如果你用的其他IP,比如外网的MONGO,那么要加上 --host xxx.xxx.xx.xx 具体IP

当你看到下面的内容,说明连接上了,距离你完成副本集搭建只有一步之遥了。

// 在mongo 上切换到admin数据库,执行
use admin
// 然后会提示你 switched to db admin
// 这个时候输入以下配置

> config={_id:"myrs",members:[{_id:0,host:"localhost:27017"},{_id:1,host:"localhost:27018"},{_id:2,host:"localhost:27019"}]}

补充说明,第一个_id的值是你在前面配置的副本集名称,要保持一致。 如果你的三台服务器都是本地的,可以用localhost,当然也可以用你服务器的IP地址。

执行完毕后,页面会出来如下图内容,我执行的时候忘了截图保存,所以拿别人的来替代下。

然后最后执行初始化生效

# 注意,这里的config 就是上一步命令,你定义的时候,用的变量名,要一致。  
rs.initiate(config)

执行初始化成功后,页面会提示ok.

Ok,至此你已经完成副本集配置,可以做测试验证看看了。

可以执行查询看看副本集状态

rs.status()

看到这些信息就说明配置好了

补充:如果想要查看当前MongoDB是否正常在后台跑,可以输入 ps aux |grep mongodb  查找是否对应进程。

如果需要关掉某个进程重启,可以 kill  进程ID  (通过ps aux |grep mongodb 可以找到)

再启动,还是在mongodb bin路径下 执行 mongod -f /具体存放路径/mongodb.conf  -f 表示fork进程 后台运行

步骤五:连接测试

可以用mongo compass / studio 3t 或者直接在虚拟机上面操作连接。

分别连接主节点和2个子节点。

这个时候你去主节点插入数据,然后到子节点就可以看到一样的。

然后子节点里面是写入不了的,会提示只能在主节点写入。

注意:要确保服务器的端口开放,不然连不上去。

至此,副本集的基本搭建已完成。

希望这篇文章能帮到你~