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

news/2023/6/9 18:45:44

本文由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 Processmove_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, 默认保存在指定的位置.通过修改了配置文件的存放路径,让前端能够获取到配置的路径,才解决了这个问题.

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

如若内容造成侵权/违法违规/事实不符,请联系郑州代理记账网进行投诉反馈,一经查实,立即删除!

相关文章

android 手机分辨率占比,Android屏幕密度(Density)和分辨率概念详解

移动设备有大有小,那么如何适应不同屏幕呢,这给我们编程人员造成了很多困惑。我也是突然想到这些问题,然后去网上搜搜相关东西,整理如下。首先,对下面这些长度单位必须了解。看到有很多网友不太理解dp、sp和px的区别&a…

android dpi 修改,Android屏幕适配之修改density

标准:1920px * 1080 5英寸 480 3三星s10-22801080 6.1英寸 414 2.58MI mix2-19201080 5.99 368 2.29android中的dp在渲染前会将dp转为px,计算公式:px density * dp;density dpi / 160;px dp * (dpi / 160);dpi计算大致需求如下&#xff1a…

5003笔记 Statistic Chapter3-Density Estimation

Density Estimation:密度估计 Cumulative distribution:累计分布函数F(x) 注意,均值mean E(x) np, Var(x) np(1-p) Continuous distribution:连续分布 连续的边界不重要,离散的边界重要 这里连续变量的PD…

linux 修改分辨率lcd_Android中的lcd_density设置

在/system/build.prop中设置:对于HVGA屏,设置ro.sf.lcd_density160对于WVGA屏,设置ro.sf.lcd_density240下文对于lcd_density的设置讲解的很清楚。我的是800x600的屏,在Android 的Notifications没有达到满屏幕宽度原以为就是xml布…

Power Spectral Density

对于一个特定的信号来说,有时域与频域两个表达形式,时域表现的是信号随时间的变化,频域表现的是信号在不同频率上的分量。在信号处理中,通常会对信号进行傅里叶变换得到该信号的频域表示,从而得到信号在频域上的特性&a…

【机器学习sklearn】两个例子轻松搞懂核密度估计KernelDensity

前言 作业中遇到了需要使用KernelDensity的情况,但是网上的资料参差不齐,找了不短的时间却失望而归,最后还是靠着自己的理解才弄懂sklearn这个函数的使用,特此纪念。 【机器学习sklearn】两个例子轻松搞懂核密度估计KernelDensity…

android修改系统density,Android应用开发Android 修改系统默认density

本文将带你了解Android应用开发Android 修改系统默认density,希望本文对大家学Android有所帮助。如你所知在Anroid N 中,系统添加了多个级别的密度值供用户选择。系统的默认的值就是 ro.sf.lcd_density同时其他级别的默认值的大小基础也是以默认值为基础…

android density 和款高度,Android Density(密度)

1. 什么是density引用1) densitydensity表示每英寸有多少个显示点(逻辑值),它的单位是dpi:dot perinch,通常屏幕大时,density就大,屏幕小时,density就小,通常屏幕实际分辨率为240px*400px时&am…