QT文件IO

news2025/1/13 15:42:53

七、文件IO

  1. QFileDialog文件对话框

与QMessageBox一样,QFileDialog也继承了QDialog类,直接使用静态成员函数弹窗,弹窗的结果(选择的文件路径)通过函数返回值返回。

// 获取一个打开或保存的文件路径
// 参数1:父对象
// 参数2:即windwTitle属性(标题)
// 参数3:在那个目录中打开,默认值表示项目的工作目录
// 参数4:文件格式过滤器
// 返回值:选择的文件路径,如果选择失败,返回空字符
QString	getOpenFileName(
        QWidget * parent = 0, 
        const QString & caption = QString(), 
        const QString & dir = QString(), 
        const QString & filter = QString())
QString QFileDialog::getSaveFileName(
        QWidget * parent = 0, 
        const QString & caption = QString(), 
        const QString & dir = QString(), 
        const QString & filter = QString())
[static]

需要注意以下,QFileDialog只是一个窗口类,本身不具备任何IO的能力。

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,
            SLOT(btnClickedSlot()));
    connect(ui->pushButtonSave,SIGNAL(clicked()),this,
            SLOT(btnClickedSlot()));

}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::btnClickedSlot()
{
    if(ui->pushButtonOpen == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";
        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);
        if(path != "")
        {
            readPath = path;
            ui->textBrowserOpen->append(path);
        }
        else if(readPath == "")
        {
            QMessageBox::warning(this,"提示","请选择打开的文件");
        }

    }
    else if(ui->pushButtonSave == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";
        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);
        if(path != "")
        {
            writePath = path;
            ui->textBrowserSave->append(path);
        }
        else if(writePath == "")
        {
            QMessageBox::warning(this,"提示","请选择保存的文件");
        }
    }
    else if(ui->pushButtonCopy == sender())
    {

    }
}

2、QFileInfo文件信息类

只需要创建出对象后,通过各种成员函数直接获取文件信息。

// 构造函数
// 参数为文件路径,如果文件非法,仍然可以创建出QFileInfo对象
QFileInfo::​QFileInfo(const QString & file)

// 判断文件是否存在
// 如果存在则返回true。否则返回false
bool QFileInfo::​exists() const

// 返回基础名称,不包含后缀
QString QFileInfo::​baseName() const

// 获取文件大小
// 返回为文件大小
qint64 QFileInfo::​size() const

// 返回文件可读性,true可读、false不可读
bool QFileInfo::​isReadable() const

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,
            SLOT(btnClickedSlot()));
    connect(ui->pushButtonSave,SIGNAL(clicked()),this,
            SLOT(btnClickedSlot()));

}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::printFileInfo()
{
    // 创建文件对象
    QFileInfo fileInfo(readPath);

    // 判断文件是否存在
    if(!fileInfo.exists())
    {
        QMessageBox::warning(this,"提示","文件路径无效");
        return;
    }
    QString text = fileInfo.baseName();
    text.prepend("文件名称:");
    ui->textBrowserOpen->append(text);

    qint64 size = fileInfo.size();
    text = QString::number(size);
    text.prepend("文件大小:").append("字节");
    ui->textBrowserOpen->append(text);

    if(fileInfo.isReadable())
    {
        ui->textBrowserOpen->append("文件可读");
    }
    else
    {
        ui->textBrowserOpen->append("文件不可读");
    }
}

void Dialog::btnClickedSlot()
{
    if(ui->pushButtonOpen == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";
        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);
        if(path != "")
        {
            readPath = path;
            ui->textBrowserOpen->append(path);
            printFileInfo(); // 文件读取成功后输出信息
        }
        else if(readPath == "")
        {
            QMessageBox::warning(this,"提示","请选择打开的文件");
        }

    }
    else if(ui->pushButtonSave == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";
        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);
        if(path != "")
        {
            writePath = path;
            ui->textBrowserSave->append(path);
        }
        else if(writePath == "")
        {
            QMessageBox::warning(this,"提示","请选择保存的文件");
        }
    }
    else if(ui->pushButtonCopy == sender())
    {

    }

}

3、QFile文件读写类

在Qt中所有IO类都继承自QIODevice类,QIODevice类中规定了最基础的IO相关接口,这些接口虽然在不同的派生类中可能实现有所不同,但调用方式一致。

// 构造函数
// 参数为文件路径,如果是非法路径,也能创建出QFIle对象,但是不能正常IO输入输出操作。
QFile::​QFile(const QString & name)

// 判断QFile对应的文件是否存在
bool QFile::​exists() const

// 打开数据流
// 参数为打开模式、只读模式、只写模式、读写模式
bool QIODevice::​open(OpenMode mode)[virtual]

// 构造函数,QByteArray是qt中的常用数组
// 构造一个空子节数组
QByteArray::​QByteArray()

// 是否读取到文件末尾
bool QIODevice::​atEnd() const[virtual]

// 读取数据
// 参数:每次读取的大小
// 返回值QByteArray字符数组
QByteArray QIODevice::​read(qint64 maxSize)

// 写入数据
// 要写入的数据
// 返回值,本次写入的大小,返回值为-1表示写入失败
qint64 QIODevice::​write(const QByteArray & byteArray)

// 清空缓冲区
bool QFileDevice::​flush()

// 关闭数据流
void QIODevice::​close()[virtual]

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,
            SLOT(btnClickedSlot()));
    connect(ui->pushButtonSave,SIGNAL(clicked()),this,
            SLOT(btnClickedSlot()));
    connect(ui->pushButtonCopy,SIGNAL(clicked()),this,
            SLOT(btnClickedSlot()));

}

Dialog::~Dialog()
{
    delete ui;
}




void Dialog::printFileInfo()
{
    // 创建文件对象
    QFileInfo fileInfo(readPath);

    // 判断文件是否存在
    if(!fileInfo.exists())
    {
        QMessageBox::warning(this,"提示","文件路径无效");
        return;
    }
    QString text = fileInfo.baseName();
    text.prepend("文件名称:");
    ui->textBrowserOpen->append(text);

    qint64 size = fileInfo.size();
    text = QString::number(size);
    text.prepend("文件大小:").append("字节");
    ui->textBrowserOpen->append(text);

    if(fileInfo.isReadable())
    {
        ui->textBrowserOpen->append("文件可读");
    }
    else
    {
        ui->textBrowserOpen->append("文件不可读");
    }
}

void Dialog::copy()
{
    if(readPath == "")
    {
        QMessageBox::warning(this,"提示","请选择要读取的文件");
        return;
    }
    if(writePath == "")
    {
        QMessageBox::warning(this,"提示","请选择要写入的文件");
        return;
    }
    // 正在拷贝时屏蔽拷贝按钮
    ui->pushButtonCopy->setEnabled(false);

    // 创建QFile对象
    QFile readFile(readPath);
    QFile writeFile(writePath);

    // 打开文件流
    readFile.open(QIODevice::ReadOnly); // 只读模式
    writeFile.open(QIODevice::WriteOnly); // 只写模式

    // 添加进度条进度
    qint64 totalSize = readFile.size(); // 获取文件总大小
    qint64 hasRead = 0; // 已经读写的大小

    QByteArray array;
    while(!readFile.atEnd())
    {
        array = readFile.read(1024); // 每次读取1kb
        qint64 writeRet = writeFile.write(array); // 写入数据,返回值为本次写入的大小
        if(writeRet == -1) // 如果写入数据的返回值为-1表示写入失败
        {
            QMessageBox::critical(this,"错误","文件拷贝失败");
            return;
        }
        // 将写出的数据设置给进度条
        hasRead += writeRet; // 获取已经写入总大小
        int per = hasRead *100/totalSize; // 计算百分比
        ui->progressBar->setValue(per); // 设置给进度条
    }

    // 清空缓存区
    writeFile.flush();

    // 关闭数据流
    readFile.close();
    writeFile.close();

    // 拷贝完成后解除按钮屏蔽
    ui->pushButtonCopy->setEnabled(true);
    // 添加拷贝完成提示框
    QMessageBox::information(this,"提示","拷贝完成");
}

void Dialog::btnClickedSlot()
{
    if(ui->pushButtonOpen == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";
        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);
        if(path != "")
        {
            readPath = path;
            ui->textBrowserOpen->append(path);
            printFileInfo(); // 文件读取成功后输出信息
        }
        else if(readPath == "")
        {
            QMessageBox::warning(this,"提示","请选择打开的文件");
        }

    }
    else if(ui->pushButtonSave == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.pro *.ui)";
        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);
        if(path != "")
        {
            writePath = path;
            ui->textBrowserSave->append(path);
        }
        else if(writePath == "")
        {
            QMessageBox::warning(this,"提示","请选择保存的文件");
        }
    }
    else if(ui->pushButtonCopy == sender())
    {
        copy();
    }

}

[思考]上面的代码有没有问题?

当拷贝大文件时,会出现卡顿,如果尝试关闭,则触发:

4、UI与耗时操作

在默认情况下,Qt的项目是单线程,这个自带的线程用于处理程序的主要任务和UI交互也被称为主线程或UI线程。

如果在主线程中执行耗时操作(IO或复杂算法)会导致主线程原本执行的操作被阻塞,甚至无法关闭,形成”假死“的现象。

当操作系统发现某个进程无法被正常关闭时,会弹出程序未响应窗口引导用户选择是否强制关闭当前进程。

解决方法是使用多线程。

5、QThread线程类

5.1 复现程序未响应

QThread类是Qt的线程类,

// 强制线程休眠msecs个毫秒
void	msleep(unsigned long msecs)

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    connect(ui->pushButtonSleep,SIGNAL(clicked()),
            this,SLOT(btnSleepClickedSlot()));
    connect(ui->pushButtonClose,SIGNAL(clicked()),
            this,SLOT(close()));

}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::btnSleepClickedSlot()
{
    qDebug() << "开始睡觉" ;
    QThread::msleep(15000);
    qDebug() << "睡眠结束" ;
}

5.2 创建并启动一个子线程

主线程以外的线程都是子线程,子线程不能执行主线程的UI操作。只能执行耗时操作。

下面是创建并启动一个自定义子线程的步骤:

  1. 在Qt Creator中选中项目名称,鼠标右键,点击添加新文件。

  1. 在弹出的窗口中,先设置类名,然后在选择基类名称QObject,最后点击”下一步“。

  1. 在项目管理界面,直接点击完成,可以看到线程类的文件已经创建。

  1. 选择新建的头文件,把继承的QObject更改为QThread

  1. 选择新建的.Cpp文件,把透传构造的QObject更改为QThread

  1. 在自定义线程中,覆盖基类QThread的run函数。

// 此函数是子线程执行的起始点,也是子线程的结束点。
void QThread::​run()[vritual protected]

7、在run函数的函数体中编写子线程要执行的耗时操作。

  1. 在主线程中创建子线程并启动

// 启动子线程,调用此函数后,会在子线程中自动执行run函数
// 参数欸子线程执行的优先级,默认值为创建所在的线程相同优先级
void QThread::​start(Priority priority = InheritPriority)[slot]

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    connect(ui->pushButtonSleep,SIGNAL(clicked()),
            this,SLOT(btnSleepClickedSlot()));
    connect(ui->pushButtonClose,SIGNAL(clicked()),
            this,SLOT(close()));
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::btnSleepClickedSlot()
{
     // 创建子线程对象
     MyThread *mt = new MyThread(this);
     // 启动子线程
     mt->start();
}

5.3 异步刷新

在实际的开发中,两个线程不可能毫无关系的前提下各干各的,最常见的情况是主线程分配一个耗时任务给子线程,子线程需要把耗时任务的执行情况反馈给主线程。主线程刷新子线程耗时的操作,并展示对应的UI效果。

【例子】:子线程执行文件拷贝,主线程显示拷贝的进度。

通常子线程是主线程对象的子对象,因此异步刷新就是对象通信的问题。使用信号槽解决。

咱们写一个简单的例子,一个伪拷贝的案例,使用for循环加睡眠, 模拟文件拷贝的功能。

今晚作业:真正的子线程拷贝文件。

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    connect(ui->pushButton,SIGNAL(clicked()),
            this,SLOT(btnSleepClickedSlot()));
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::btnSleepClickedSlot()
{
    ui->pushButton->setEnabled(false);
     // 创建子线程对象
     MyThread *mt = new MyThread(this);
     connect(mt,SIGNAL(valueSignal(int)),this,
             SLOT(valueSlot(int)));
     // 启动子线程
     mt->start();
}

void Dialog::valueSlot(int value)
{
    ui->progressBar->setValue(value);
    if(value == 100)
    {
        ui->pushButton->setEnabled(true);
        QMessageBox::warning(this,"提示","拷贝完成");
    }
}

问题:频繁抖动窗口,会出现焦点抢夺问题,导致卡死。使用hide函数,来隐藏主窗口。

5.4 线程停止

子线程往往执行耗时操作,耗时操作又往往伴随着循环,因此并不建议使用粗暴的方式直接停止线程,因为强行停止线程会导致耗时操作资源无法回收等问题。

可以通过给循环设置标志位的方式使线程停止。

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    connect(ui->pushButton,SIGNAL(clicked()),
            this,SLOT(btnSleepClickedSlot()));
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::btnSleepClickedSlot()
{
    if(ui->pushButton->text() == "开始拷贝")
    {
        // 创建子线程对象
        mt = new MyThread(this);
        connect(mt,SIGNAL(valueSignal(int)),this,
                SLOT(valueSlot(int)));
        // 启动子线程
        mt->start();
        ui->pushButton->setText("停止拷贝");
    }
    else if(ui->pushButton->text() == "停止拷贝")
    {
        ui->pushButton->setText("开始拷贝");
        mt->setRunningState(false);
    }

}

void Dialog::valueSlot(int value)
{
    ui->progressBar->setValue(value);
    if(value == 100)
    {
        this->hide(); // 隐藏主窗口
        this->show(); // 显示主窗口
        QMessageBox::warning(this,"提示","拷贝完成");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1470999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Git+py+ipynb Usage

0.default config ssh-keygen -t rsa #之后一路回车,当前目录.ssh/下产生公私钥 cat ~/.ssh/id_rsa.pub #复制公钥到账号 git config --global user.email account_email git config --global user.name account_namebug of ipynb TqdmWarning: IProgress not found. Please …

【JavaEE】_HttpServlet类

目录 1. init方法 2. destory方法 3. service方法 4. servlet生命周期 前文已经提及到&#xff1a;servlet是tomcat提供的&#xff0c;用于操作HTTP协议的一组API&#xff0c;可以将这组API理解为HTTP服务器的框架&#xff1b; 编写一个servlet程序&#xff0c;往往都要继…

记一次pulsar数据丢失排查

记一次pulsar数据丢失排查 背景 生产者往pulsar写消息时会有递增的序列号字段&#xff0c;消费端在消费时&#xff0c;会出现序列号断层。当下无法确定是生产端、mq、消费端哪个地方丢失了数据&#xff0c;所以先从生产端进行排查。 生产端的消息发送是通过sendAsync的异步方…

基于插件实现RabbitMQ“延时队列“

1.官网下载 在添加链接描述下载rabbitmq_delayed_message_exchange 插件,本文以v3.10.0为例 1.1.上传安装包 scp /Users/hong/资料/rabbitmq_delayed_message_exchange-3.10.0.ez root10.211.55.4:/usr/local/software1.2.将文件移入RabbitMQ的安装目录下的plugins目录 m…

EasyRecovery2024个人免费版本电脑手机数据恢复软件下载

EasyRecovery是一款功能强大的数据恢复软件&#xff0c;能够帮助用户恢复丢失、删除、格式化或损坏的数据。无论是由于误操作、病毒攻击、硬盘故障还是其他原因导致的数据丢失&#xff0c;EasyRecovery都能提供有效的解决方案。 该软件支持从各种存储介质恢复数据&#xff0c;…

linux-并发通信

一.linux-tcp通信框架 1.基础框架 1.1 tcp 服务器框架 1.套接字 #include <sys/socket.h> int socket(int domain, int type, int protocol);
 返回的文件描述符可以指向当前的socket&#xff0c;后续通过对文件描述符的访问就可以配置这个socket 成功时返回文件…

云原生应用测试:挑战与方法

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

Linux进程 ----- 信号处理

前言 从信号产生到信号保存&#xff0c;中间经历了很多&#xff0c;当操作系统准备对信号进行处理时&#xff0c;还需要判断时机是否 “合适”&#xff0c;在绝大多数情况下&#xff0c;只有在 “合适” 的时机才能处理信号&#xff0c;即调用信号的执行动作。 一、信号的处理…

万界星空科技MES系统,实现数字化智能工厂

万界星空科技帮助制造型企业解决生产过程中遇到的生产过程不透明&#xff0c;防错成本高&#xff0c;追溯困难&#xff0c;品质不可控&#xff0c;人工效率低下&#xff0c;库存积压&#xff0c;交期延误等问题&#xff0c;从而达到“降本增效”的目标。打通各个信息孤岛&#…

Python性能测试框架Locust实战教程

01、认识Locust Locust是一个比较容易上手的分布式用户负载测试工具。它旨在对网站&#xff08;或其他系统&#xff09;进行负载测试&#xff0c;并确定系统可以处理多少个并发用户&#xff0c;Locust 在英文中是 蝗虫 的意思&#xff1a;作者的想法是在测试期间&#xff0c;放…

推荐一个 Obsidian 的 ChatGPT 插件

源码地址&#xff1a;https://github.com/nhaouari/obsidian-textgenerator-plugin Text Generator 是目前我使用过的最好的 Obsidian 中的 ChatGPT 功能插件。它旨在智能生成内容&#xff0c;以便轻松记笔记。它不仅可以在 Obsidian 中直接使用 ChatGPT&#xff0c;还提供了优…

Vue+SpringBoot打造衣物搭配系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 衣物档案模块2.2 衣物搭配模块2.3 衣物收藏模块 三、系统设计3.1 用例设计3.2 E-R图设计3.3 数据库设计3.3.1 衣物档案表3.3.2 衣物搭配表3.3.3 衣物收藏表 四、系统实现4.1 登录页4.2 衣物档案模块4.3 衣物搭配模块4.4…

力扣用例题:2的幂

此题的解题方法在于根据用例调整代码 bool isPowerOfTwo(int n) {if(n1){return true;}if(n<0){return false;}while(n>2){if(n%21){return false;}nn/2; }if(n1){return false;}return true;}

RDMA内核态函数ib_post_send()源码分析

最近调用linux内核下RDMA的Verb API ib_post_send()出现了问题&#xff0c;因此从源码分析一下这个函数的调用过程。 我使用的内核版本为5.15.0-94 这是函数ib_post_send的头文件定义&#xff0c;这个函数的意义是向发送队列提交发送请求&#xff0c;他会调用qp对应设备的post_…

C# EF Core迁移数据库

现象&#xff1a; 在CodeFirst时&#xff0c;先写字段与表&#xff0c;创建数据库后&#xff0c;再添加内容 但字段与表会变更&#xff0c;比如改名删除增加等 需求&#xff1a; 当表字段变更时&#xff0c;同时变更数据库&#xff0c;执行数据库迁移 核心命令 Add-Migrat…

一种基于道路分类特性的超快速车道检测算法

摘要&#xff1a; 本文介绍了一种新颖、简单但有效的车道检测公式。 车道检测是自动驾驶和高级驾驶员辅助系统 (ADAS) 的基本组成部分&#xff0c;在实际高阶驾驶辅助应用中&#xff0c;考虑车道保持、转向、限速等相关的控制问题&#xff0c;这种方式通常是通过受限的车辆计算…

java——多线程基础

目录 线程的概述多线程的创建方式一&#xff1a;继承Thread类方式二&#xff1a;实现Runnable接口方式三&#xff1a;利用Callable接口、FutureTask类来实现。Thread常用的方法 线程安全问题线程安全问题概述线程安全问题案例取钱案例描述模拟代码如下&#xff1a;执行结果 线程…

2024-02-25 Unity 编辑器开发之编辑器拓展7 —— Inspector 窗口拓展

文章目录 1 SerializedObject 和 SerializedProperty2 自定义显示步骤3 数组、List 自定义显示3.1 基础方式3.2 自定义方式 4 自定义属性自定义显示4.1 基础方式4.2 自定义方式 5 字典自定义显示5.1 SerizlizeField5.2 ISerializationCallbackReceiver5.3 代码示例 1 Serialize…

【Activiti7系列】Activi7简介和基于Spring Boot整合Activiti7(流程设计器)

本文将介绍Activiti7基础概念及基于Spring Boot整合Activiti7(流程设计器)的具体步骤。 作者&#xff1a;后端小肥肠 1. 前言 在企业级应用中&#xff0c;业务流程的管理和执行是至关重要的一环。Activiti7是一个强大的开源工作流引擎&#xff0c;它提供了灵活的流程定义、任务…

linux---安使用nginx

目录 一、编译安装Nginx 1、关闭防火墙&#xff0c;将安装nginx所需要软件包传到/opt目录下 ​编辑2、安装依赖包 3、创建运行用户、组 4、编译安装nginx 5、创建软链接后直接nginx启动 ​编辑 6、创建nginx自启动文件 ​编辑6.1 重新加载配置、设置开机自启并开启服务…