哎,半年不更博了,惭愧。从去年9月份开始,由于公司需要,转成了web app开发。直到年后回来才又回到嵌入式开发。学习web app开发过程中,也深深爱上了前端技术。那种敏捷开发自然是嵌入式不能比的,有段时间甚至感到还是app开发比较接地气,嵌入式大半年做不出一个产品,做出来还可能是面向某个领域的小众产品。不过从年后回来开始调wifi,又感觉技术上还是嵌入式比较有成就感,当然,技术无高低,哪项技术都不容易。学习web app时候一直没敢写博客,主要是因为自己也在学习阶段,怕水平太低误导群众。不过经过半年学习,我也算是踏进了半只脚的前端了,以后也会记录写web相关的内容。当然,还是以我熟悉的嵌入式为主啦。
废话不多说,开始正式新旅程吧。前阵子借助wpa_supplicant库实现了模拟手机wifi的连接程序,可谓是步步荆棘。这个待我好好整理后再发出来。这次先记录一个很小的功能—在linux平台的qt环境下实现文件夹拷贝并且带进度显示的几种方案。
项目需求是实现一个自升级功能,其实就是用新的文件夹内容覆盖原来的,但是需要有进度显示。
- 纯用系统命令
首先想到的就是借助Linux本身的cp命令啦,这个复制是没什么问题的,但是进度怎么拿到就是问题了。于是去查看cp的帮助信息,发现并没有拷贝进度相关的反馈。
然后笔者又去网上搜“linux 拷贝 进度”等关键词,搜到下面一篇文章:
http://www.veryhuo.com/a/view/18239.html
文中提到一个新的命令:rsync,用法如下:
$ rsync --progress a.exe a1.exe
a.exe
20461680 100% 38.35MB/s 0:00:00 (xfer#1, to-check=0/1)
sent 20464246 bytes received 31 bytes 13642851.33 bytes/sec
total size is 20461680 speedup is 1.00
笔者把a.exe复制成a3,命令下面的是输出信息。加了progress 选项输出内容还是非常详细的,包括进度,速度等。进度和速度都是不断变化的。所以只要提取命令的输出信息即可得到拷贝的进度。那么如何得到linux命令的反馈信息呢?这里我们可以使用linux的管道–pipe来实现。提供一段参考代码:
char num[5];
char command[100] = "rsync --progress a.zip a4";
int main()
{
char buf[200];
FILE *pipe;
char *point;
if((pipe = popen(command, "r")) == NULL)
{
printf("err popen");
return -1;
}
while(1){
if(fgets(buf, 100, pipe) != NULL)
{
buf[strlen(buf)-1] = '\0';
if(point = strstr(buf, "%")){
printf("%s\n", buf); //buf里存的就是反馈信息
}
}
else
{
break;
}
usleep(2);
}
pclose(pipe);
}
剩下的就很简单了,只要提取%前面的数字就可以了,这里推荐使用正则表达式。
本以为做到这里就能实现了,结果使用这个命令拷贝文件夹时候发现它并不是输出整体的进度,而是输出每个文件的拷贝进度,这样肯定是不符合我们需求的。所以,这种方案只适合单个文件,并不适合文件夹。
- 命令+线程
既然纯用命令不行,笔者又想了个迂回方案。用cp+线程实现。具体思想如下:
- 先记录待拷贝文件夹总大小
- 执行cp命令开始拷贝
- 开始拷贝后立刻启动一个线程,线程工作就是不停计算拷贝的文件夹大小,然后和记录的总大小计算进度,返回给主进程处理。
这里有个点就是计算文件夹大小,可以用递归算法来实现。提供一段qt的参考代码:
quint64 ProgressThread::du(const QString &path)
{
QDir dir(path);
quint64 size = 0;
foreach (QFileInfo fileInfo, dir.entryInfoList(QDir::Files))
{
size += fileInfo.size();
}
foreach (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
size += du(path + QDir::separator() + subDir);
}
return size;
}
path是待文件夹路径,实测过程中还有一个问题,由于cp命令是阻塞式,所以是以先执行命令,后开启统计线程的顺序执行,会造成拷贝完了以后线程才启动。所以可以先开启线程,再执行拷贝,当然最好的方案应该是两个操作都处理成线程,这样不用担心阻塞问题,还不会影响QT的UI响应。
- 不借助命令
也就是纯程序实现了,这个无非是把cp的命令转换为程序实现,基本上就是递归拷贝,然后每拷贝完一个文件调用上面的统计程序进行计算拷贝进度。笔者没有亲测,只是预想了方案,当然,论灵活性当然是这个方案最高。但是没必要再造轮子不是吗,第二种方案还是比较简单可行的。