[疑难杂症2023-002]不就是Move一个文件吗,怎么会有这么多坑呢?

Posted inter_peng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[疑难杂症2023-002]不就是Move一个文件吗,怎么会有这么多坑呢?相关的知识,希望对你有一定的参考价值。

本文由Markdown语法编辑器编辑完成.

1. 前言:

近期在项目中遇到一个需求.
背景是,在一个QT封装的C/S架构的软件中,一个报告的预览页面,是由QT封装了QWebWidget, 里面放着一个网页.这个网页通过调用一定的逻辑,可以将当前看到的网页,生成一个pdf, 存储到一个路径下面.

由于前端在执行js(调用jsPdf库)时,无法设置存储路径,因此只能存储在软件安装的当前目录下面.比如,这个C/S架构的软件,是安装在了D盘下面的某个文件夹中.

但是,我们希望这个报告的pdf, 默认生成在C盘的一个指定目录下面.

因此,这个需求概括起来就是:
a> 前端默认将pdf生成在了D盘的某一个路径下面;
b> 后端如何在pdf生成后,将这个pdf文件,挪到用户指定的C盘的某个路径下面.

2. 解决方案探索:

2.1 前端通知后端,后端move到指定位置

根据需求,很容易想到的解决方案是,前端在生成pdf后,通知一下后端.后端去指定路径下面,找到生成的pdf, 通过os.move()或shutil.move()的指令,将pdf挪到到指定位置即可.
但是在实际的测试中,会遇到很多意向不到的问题.

2.1.1 前端异步生成pdf

由于前端在调用jsPdf组件生成pdf的操作,是一个异步操作.
因为生成报告的pdf时间很长,大约有80 ~ 120M左右.因此,前端不可能做成同步操作,只能是异步来进行.
这就会造成,前端在发出生成pdf的指令后,如果马上通知后端.
后端去指定的路径下,准备移动这个文件时,这个文件可能压根还没有生成出来呢,还是计算机的内存中呢.于是就会报"FileNotFound"之类的异常.

2.1.2 后端增加等待机制

为了解决FileNotFound的问题,后端能够想到的方法,自然是增加等待机制.通过轮询,每隔一定时间去查看一下pdf文件是否生成.比如,增加while循环.

import os
import shutil
......
while:
	if not os.path.exists("报告1.pdf"):
		sleep(1)
	else:
		break
shutil.move("D:/报告1.pdf", "C:/target")

但是实际测试时,会遇到进程阻塞的问题.
由于Python默认是单进程执行的.因此当这里增加了while循环这样的指令后,进程就会一直卡在这里.
客户端无法执行任何其他的操作,出现未响应之类的问题.

另一种方法,当然是通过创建多进程的方法.
比如,把判断pdf是否存在和挪动pdf, 放在另一个Process里面,不要影响主进程.
大致代码如下:

import os
from multiprocessing import Process

move_pdf_process = Process(target=move_pdf_file, args=("D:/报告1.pdf", ))
move_pdf_process.start()

def move_pdf_file(file_path):
	while:
		if not os.path.exists(file_path):
			sleep(1)
		else:
			shutil.move(file_path, "C:/target")

通过这种机制,解决了移动文件时,文件不存在的问题.
但是又会遇到新的问题.

2.1.3 文件正在写入,移动后文件不完整或为空

上面的方法,虽然解决了文件移动时,文件还不存在造成的问题.
但是之前忽略的一个问题时,这个文件虽然落盘了,但是文件可能还正在被写入中.因为文件内容很多,它不是一次性被写入的,而是有一个过程.
于是,就需要想办法来判断,这个文件什么时候写完了.
我尝试了两种方法吧,第1种是,每隔1秒,判断文件的大小有没有发生改变; 第2种是,每隔1秒,判断文件的修改时间有没有发生改变.
但是,这两种方法,在实际的使用中,还是会遇到问题.
比如,即使判断出,这个文件的大小和更新时间已经不变了,但是挪动后,查看文件还是为空.

2.1.4 通过创建硬链接的方式

在经历了上述的尝试后,直接挪动文件的方案,基本上被否决了.
后来,我想到的方法是创建硬链接.也就是将文件初始生成的路径,和想要挪到的目标路径,创建硬链接.
这样,原始文件的任何变化,都会同步到目标路径下.
创建硬链接的方式如下:

import os
os.link("D/报告1.pdf", "C/target/报告1.pdf")

但是,运行这条指令,在windows操作系统下,也会报错.
后来查询了一下,windows上,如果是在同一盘符下运行os.link没问题,但是跨越盘符运行时,会报错.
因此,这个方案也被否决了.

结论:

经过接近两三天的尝试,我最初的方案,被否决了.

虽然,独立进行功能测试时,比如,首先把一个现成的pdf文件,放到D盘,再运行shutil.move()来移动这个pdf文件到C盘,是完全没问题的.但是,在做集成测试,也就是前后端真正联调时,却会发生很多意想不到的状况.

因此,在涉及到前后端相互调用的功能时,一定要尽早地进行联调测试,不能仅满足于做模拟测试,或用mock数据测试.
同时,也是需要多积累相关的经验,越是感觉简单的功能,其实可能藏着很多的坑,要多思考各种情况,避免在软件交付的最后时刻,发生功能不可用的block级别的bug, 这会严重影响软件的顺利交付.

最后,由于自己的方案无法很好地解决该问题.后来,还是回归到本质问题:就是如何能够让前端生成的报告pdf, 默认保存在指定的位置.通过修改了配置文件的存放路径,让前端能够获取到配置的路径,才解决了这个问题.

以上是关于[疑难杂症2023-002]不就是Move一个文件吗,怎么会有这么多坑呢?的主要内容,如果未能解决你的问题,请参考以下文章

直播疑难杂症排查— 音画不同步

《直播疑难杂症排查系列》之一 :播放失败

直播疑难杂症排查— 播放失败

直播疑难杂症排查— 拖动不准

uniapp引入font-awsome字体图标-疑难解决

关于DOS下的MOVE命令..