基于QT开发PC端文件加密工具
1、客户需求
某案子。该方案,通过外挂TF卡实现,本地教育资源的调取使用。按客户需求,把TF卡内的内容进行加密,并在软件中写好解密,确保资源文件只能在教育机中才能正常播放,以防被拷贝复制。
2、涉及的加密文件类型
目前仅支持的加密文件类型:文本(.txt)、图片(.png/.jpg)、视频(.mp4)、音乐(.mp3)
3、开发环境
QT5.12(C++)、Qt程序打包发布方法(使用官方提供的windeployqt工具)
4、设计思路
如图所示,为加密工具的操作界面。操作方法:1、选择待加密文件所在的文件夹;2、选择指定加密文件的新文件夹;3、列出所有待加密的文件;4、一键加密。
软件设计思路:
1、选择待加密文件所在的文件夹
首先是利用QT的槽函数机制,浏览控件都有对应的槽函数。
void MainWindow::on_SelectSourceFilepushButton_3_clicked()
{
//获取应用的当前路径,并通过QString类定义的变量保存该路径
QString curPath = QCoreApplication::applicationDirPath();
//设置选择弹框的标题
QString dlaTitle = "选择待加密文件";
QString selectedDir = QFileDialog::getExistingDirectory(this, dlaTitle, curPath, QFileDialog::ShowDirsOnly);
//通过全局变量保存待加密文件的文件路径
SourceFilePath = selectedDir;
if(!selectedDir.isEmpty()){
//通过comboBox控件显示选择的指定文件夹路径
ui->comboBox->addItem(selectedDir);
//初始化进度条为0
ui->progressBar->setValue(0);
}
}
分析:类MainWindow为主界面,成员函数on_SelectSourceFilepushButton_3_clicked,亦为浏览控件的槽函数。
2、选择指定加密文件的新文件夹
void MainWindow::on_SelectTargetFile_pushButton_clicked()
{
//获取应用的当前路径,并通过QString类定义的变量保存该路径
QString curPath = QCoreApplication::applicationDirPath();
QString dlaTitle = "选择目标文件";
//选择指定文件夹,并保存该文件夹的路径
QString selectedDir = QFileDialog::getExistingDirectory(this, dlaTitle, curPath, QFileDialog::ShowDirsOnly);
//通过全局变量来保存指定文件夹的文件路径
TargetFilePath = selectedDir;
if(!selectedDir.isEmpty()){
//通过comboBox_2控件显示选择的指定文件夹路径
ui->comboBox_2->addItem(selectedDir);
}
}
分析:类MainWindow为主界面,成员函数on_SelectTargetFile_pushButton_clicked,亦为浏览控件的槽函数。
3、列出所有待加密的文件
void MainWindow::on_ListAllFileButton_clicked()
{
QString curPath = SourceFilePath; //获取待加密文件的文件路径
QStringList fileList; //用字符串列表的类定义对象fileList
QString filter;
ListWidget TmpListWight; //列表窗口的类定义对象TmpListWight
ui->listWidget_2->clear(); //清除主界面上的列表窗口
totalfiles = 0; //初始化用于记录总待加密文件数的变量
file_info_list = TmpListWight.GetFileList(curPath); //根据文件路径,直接获取所有文件的路径信息
//循环遍历 file_info_list对象中所有的路径,并获取文件信息fileinfo
foreach(QFileInfo fileinfo, file_info_list)
{
filter = fileinfo.suffix(); //获取文件后缀名
//过滤文件
if( (filter != "jpg") && (filter != "jpeg")
&&(filter != "png") && filter != "mp3"
&& filter != "txt" && filter != "mp4") {
continue;
}
//将每个文件路径添加到列表中
fileList.append(fileinfo.absoluteFilePath());
//在listWidget_2显示
ui->listWidget_2->addItem(fileinfo.absoluteFilePath());
}
}
4、一键加密
void MainWindow::on_EncryptionButton_clicked()
{
//创建加密文件的线程
newThread();
}
void MainWindow::newThread()
{
ThreadEncryptionFile *NewThreadFile = new ThreadEncryptionFile;
QObject::connect(NewThreadFile,SIGNAL(UpdateProgressBar(double)),this,SLOT(processbar(double)));
//启动线程,开始执行工作线程的任务
NewThreadFile->start();
}
分析:成员变量默认是界面的子类了,不会再生成一个线程。new出来的话就是在堆中,默认是一个新进程。这里注意用new来创建新线程。connect()函数有且只能在QObject类里面和QObject派生类里面使用,自己新建的类里面(基类不是QObject类和其QObject派生类)使用connect()函数是无效的,编译时会报错。新建工程比如widget,mainwindow,dialog都是QObject的派生类,所以可以直接使用connect()函数,实现信号与槽机制。NewThreadFile->start()启动线程。
重点说下自定义的加密类:
//继承 线程的加密类
class ThreadEncryptionFile :public QThread
{
Q_OBJECT
public:
bool ReadFile(const QString& SourceFileName, const QString& TargetFileName);
bool CopyDirectoryFiles(const QString &fromDir,const QString &toDir);
bool getFileCount( const QString& sPath );
void Encryption_copy(const QString& SourceFileName, const QString& TargetFileName);
void EncryptionTextFile(const QString& SourceFileName, const QString& TargetFileName);
void EncryptionMusicFile(const QString& SourceFileName, const QString& TargetFileName);
void EncryptionPhotoFile(const QString& SourceFileName, const QString& TargetFileName);
void EncryptionMovieFile(const QString& SourceFileName, const QString& TargetFileName);
QByteArray FileToByteArray(const QString& SourceFileName);
U8 word_encrypt(U8 val, U8 Secretkey);
//信号 用来通知更新 进度条
signals:
void UpdateProgressBar(double );
protected:
void run();
};
分析:QThread是线程类,是实现多线程操作的核心类,一般从QThread继承定义自己的线程类,本设计就是用的这种方式。在从QThread继承一个自定义类,并重定义虚函数run(),在run()函数里实现线程需要完成的任务,该线程可以通过调用start()开始执行工作线程的任务。
再来看看主界面的类:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_EncryptionButton_clicked();
void on_ListAllFileButton_clicked();
void on_SelectTargetFile_pushButton_clicked();
void on_SelectSourceFilepushButton_3_clicked();
void processbar(double index);
void newThread();
private:
Ui::MainWindow *ui;
QProgressBar *progressBar;
public:
QFileInfoList file_info_list;
};
分析:主窗口的类,主要是各个控件的槽函数。以及关联进度条,和文件信息列表类。
重点->加密线程中的执行内容:
void ThreadEncryptionFile::run()
{
getFileCount(SourceFilePath); //获取待加密文件总数
CopyDirectoryFiles(SourceFilePath, TargetFilePath); //拷贝目录文件,同时完成加密工作
}
获取待加密文件总数的方法如下:
bool ThreadEncryptionFile::getFileCount(const QString &sPath)
{
QDir dir( sPath ); //目录类实例化对象dir
QFileInfoList fileInfoList = dir.entryInfoList(); //文件信息列表类实例化对象fileInfoList,并初始化
foreach ( QFileInfo fileInfo, fileInfoList ) {
if ( fileInfo.fileName() == "." || fileInfo.fileName() == ".." ) {
continue;
}
totalfiles++; //统计待加密文件总数
qDebug() << "totalfile:" << totalfiles;
if (fileInfo.isDir()) {
if (!getFileCount(fileInfo.filePath()))
return false;
} //当为目录时,递归的进行
}
return true;
}
拷贝目录文件,同时完成加密工作如下:
bool ThreadEncryptionFile::CopyDirectoryFiles(const QString &fromDir,const QString &toDir)
{
QDir sourceDir(fromDir); //待加密文件所在文件夹
QDir targetDir(toDir); //指定文件夹
if(!targetDir.exists()) {
if(!targetDir.mkdir(targetDir.absolutePath())) {
return false;
}
} //指定文件夹不存在,则创建新文件夹
QFileInfoList list = sourceDir.entryInfoList();
for(int i = 0; i < list.size(); i++) {
QFileInfo fileInfo = list.at(i);
if(fileInfo.fileName()=="." || fileInfo.fileName()=="..") {
continue;
}
//如果是目录文件,则创建目录
if(fileInfo.isDir()) {
if(!CopyDirectoryFiles(fileInfo.filePath(),targetDir.filePath(fileInfo.fileName()))) {
return false;
}
} else {
if(targetDir.exists(fileInfo.fileName())) {
//有相同的,直接删除
targetDir.remove(fileInfo.fileName());
}
//拷贝完文件后,加密文件
Encryption_copy(fileInfo.filePath(), targetDir.filePath(fileInfo.fileName()));
}
//统计完成文件数量
FlishFile++;
//通过信号通知界面进度条刷新进度显示
emit UpdateProgressBar(FlishFile);
qDebug() << "FlishFile:" << FlishFile;
}
return true;
}
重点中的重点:拷贝完文件&加密文件!!! -> Encryption_copy
void ThreadEncryptionFile::Encryption_copy(const QString& SourceFileName, const QString& TargetFileName)
{
QFileInfo TmpFileInfo(SourceFileName);
ThreadEncryptionFile EncryFile;
//通过文件信息,判断待加密的文件的类型
if(!TmpFileInfo.isFile()){
return;
}
QString TmpSuffix = TmpFileInfo.suffix();
if(TmpSuffix == "txt"){
//待加密文件为文本文件
EncryFile.EncryptionTextFile(SourceFileName, TargetFileName);
} else if(TmpSuffix == "jpg" || TmpSuffix == "png"){
//待加密文件为图片文件
EncryFile.EncryptionPhotoFile(SourceFileName, TargetFileName);
} else if(TmpSuffix == "mp3") {
//待加密文件为音频文件
EncryFile.EncryptionMusicFile(SourceFileName, TargetFileName);
} else if(TmpSuffix == "mp4") {
//待加密文件为视频文件
EncryFile.EncryptionMovieFile(SourceFileName, TargetFileName);
} else {
return;
}
}
后面分别描述不同文件类型的加密方式:
文本文件的加密:
/* text [1]所有数据异或^0xD8 [2]增加*** */
void ThreadEncryptionFile::EncryptionTextFile(const QString& SourceFileName, const QString& TargetFileName)
{
//先处理目标文件
QFile TargetFile(TargetFileName);
if(TargetFile.exists()) {
//文件存在
} else {
//文件不存在,直接创建新文件
TargetFile.open(QIODevice::ReadWrite);
TargetFile.close();
}
//加密 copy原文件
QFile SourceFile(SourceFileName);
QByteArray btData;
btData = FileToByteArray(SourceFileName);
//所有数据异或^0x88
for(int i=0; i<btData.size(); i++){
btData[i] = word_encrypt(btData[i], g_u8SecretKey);
}
//增加***
btData.prepend("***");
if(TargetFile.open(QIODevice::ReadWrite)) {
TargetFile.write(btData);
TargetFile.close();
} else {
qDebug() << "read file faile";
}
}
图片文件的加密:
void ThreadEncryptionFile::EncryptionPhotoFile(const QString& SourceFileName, const QString& TargetFileName)
{
//先处理目标文件
QFile TargetFile(TargetFileName);
if(TargetFile.exists()) {
//文件存在
} else {
//文件不存在
TargetFile.open(QIODevice::ReadWrite);
TargetFile.close();
}
QByteArray btData;
QFile SourceFile(SourceFileName);
QFileInfo SourceFileInfo(SourceFileName);
//通过文件信息,判断待加密的文件的类型
if(!SourceFileInfo.isFile()){
return;
}
QString TmpSuffix = SourceFileInfo.suffix();
if(TmpSuffix == "jpg"){
//增加TOP-TECH
btData = FileToByteArray(SourceFileName);
btData.prepend("***");
//.jpg[1]遍历前640个字节[3]取反640个字节里面的0xFFD8->0xD8FF(1个就取反一个,有两个标签的话两个都要取反)
for(int i=0; i<640; i++){
if((btData[i]&0xff) == 0xff
&& (btData[i+1]&0xff) == 0xd8){
//qDebug() << "0xFFDB->0xD8FF i=" << i;
btData[i] = 0xd8;
btData[i+1] = 0xff;
}
}
//open target file
if(TargetFile.open(QIODevice::ReadWrite)) {
TargetFile.write(btData);
TargetFile.close();
} else {
qDebug() << "read file faile";
}
} else if (TmpSuffix == "png") {
//.png[1]增加***
btData = FileToByteArray(SourceFileName);
btData.prepend("***");
//open target file
if(TargetFile.open(QIODevice::ReadWrite)) {
TargetFile.write(btData);
TargetFile.close();
} else {
qDebug() << "read file faile";
}
}
}
音频文件的加密:
/* music [1]所有数据异或^0x88 [2]增加*** */
void ThreadEncryptionFile::EncryptionMusicFile(const QString& SourceFileName, const QString& TargetFileName)
{
//先处理目标文件
QFile TargetFile(TargetFileName);
if(TargetFile.exists()) {
//文件存在
} else {
//文件不存在
TargetFile.open(QIODevice::ReadWrite);
TargetFile.close();
}
QByteArray btData;
QFile SourceFile(SourceFileName);
btData = FileToByteArray(SourceFileName);
//所有数据异或^0x88
for(int i=0; i<btData.size(); i++){
btData[i] = word_encrypt(btData[i], g_u8SecretKey);
}
//增加***
btData.prepend("***");
if(TargetFile.open(QIODevice::ReadWrite)) {
TargetFile.write(btData);
TargetFile.close();
} else {
qDebug() << "read file faile";
}
}
视频文件的加密:(核心中的重点!!! -> 大文件分段处理)
void ThreadEncryptionFile::EncryptionMovieFile(const QString& SourceFileName, const QString& TargetFileName)
{
//先处理目标文件
QFile TargetFile(TargetFileName);
if(TargetFile.exists()) {
//文件存在
} else {
//文件不存在
TargetFile.open(QIODevice::ReadWrite);
TargetFile.close();
}
ReadFile(SourceFileName, TargetFileName);
}
bool ThreadEncryptionFile::ReadFile(const QString& SourceFileName, const QString& TargetFileName)
{
QFile SourceFile(SourceFileName);
QFile TargetFile(TargetFileName);
QByteArray btData;
qDebug() << "ThreadEncryptionFile::ReadFile";
//打开待加密文件
if(!SourceFile.open(QIODevice::ReadWrite)) {
qDebug() << "open file faile!";
return false;
}
//打开新建文件
if(!TargetFile.open(QIODevice::ReadWrite | QIODevice::Append)) {
qDebug() << "open file faile!";
return false;
}
//文件总大小
int TotalSize = SourceFile.size();
qDebug() << "TmpFile.size:" << SourceFile.size();
//将文件分段,每次读取的大小
int Count = TotalSize / 1024;
//先把头加密内容写到新文件中
btData.prepend("***");
TargetFile.write(btData);
if (Count <= 0) {
btData = SourceFile.read(TotalSize);
TargetFile.write(btData);
} else {
for (int i=0; i<Count; i++) {
btData = SourceFile.read(1024);
TargetFile.write(btData); //读一个段,写一段
TotalSize -= 1024;
}
if (TotalSize != 0) {
btData = SourceFile.read(TotalSize);
TargetFile.write(btData);
}
}
TargetFile.write(btData);
//将加密后的文件写入原文件,得到加密后的文件
TargetFile.close();
SourceFile.close();
return true;
}
最后的重点 -> Qt程序打包发布方法!!!
step 1: 设置工程为Release版本
step 2: 正常编译,生成build-encryption_tool-Desktop_Qt_5_12_9_MinGW_32_bit-Release文件夹
step 3:找到encryption_tool.exe文件,copy到自己新建的demo文件夹中(随便文件夹即可)
step 4: 利用官方提供的windeployqt工具,生成可发布软件(即生成dll,这样除了在自己电脑上可以使用,也可以在别人的电脑上使用)
更多推荐
基于QT开发PC端文件加密工具
发布评论