Python多进程实现文件夹的复制

背景

在多任务方面,Python比java多了一个操作进程。故而,在此用python的多进程实现一下文件夹的复制,这一功能用多线程自然也能实现。

功能实现

由于不知道要复制的文件夹里有多少文件,因此一个一个用multiprocessing.Process创建进程就不合适了。所以使用进程池来管理任务进程, 同时用任务队列保存需要拷贝的文件

先构造全局的任务队列,然后在入口方法里初始化

import os, multiprocessing

q = None

...

def main():
    ...

    global q
    q = multiprocessing.Manager().Queue() # 注意一定是Manager()的Queue(),否则子进程执行不起来
    ...

 

然后让用户输入要复制的文件夹的绝对路径,以及目标文件夹路径,并进行合法性判断(此处写死)

    dirName = "D:\\szcPython"
    newDirName = "D:\\test"
    if not os.path.isdir(dirName):
        print("无此文件夹")
        return
    if os.path.exists(newDirName):
        print("目标文件夹已存在")
        return
    os.mkdir(newDirName)

然后把要复制的文件夹里的文件(不包括目录),都作为任务加入进队列里

def getFilesCount(dirName):
    if dirName == None or not os.path.isdir(dirName): # 合法性判断
        return

    files = os.listdir(dirName) # 列出所有文件名
    for item in files:
        absolutePath = dirName + os.sep + item # 文件绝对路径的拼接
        if os.path.isdir(absolutePath):
            getFilesCount(absolutePath) # 文件夹则递归
        else: # 否则入队
            if q.full():
                print("缓存已满")
            else:
                q.put(absolutePath)

由于os.listdir()返回的列表里,元素都是文件的相对路径,因此要进行绝对路径的拼接,才能判断是否是文件夹

获取到所有要复制的文件的绝对路径后,就可以复制了,这一任务就交给多进程。

首先定义一个方法,做为多进程要执行的任务

def copyFile(destination, file, oldDir):
    if destination == None or file == None: # 合法性判断
        return
    if (not os.path.isdir(destination)) or (not os.path.exists(file)): # 合法性判断
        return

    subDir = file[file.find(oldDir) + len(oldDir) + 1:file.rfind(os.sep)] # 字符串切片,获取新文件对应的新子文件夹相对路径
    newDir = destination + os.sep + subDir # 新子文件夹绝对路径
    if not os.path.exists(newDir):
        os.mkdir(newDir)
    fileName = file[file.rfind(os.sep) + 1:] # 新文件文件名

    newFileName = newDir + os.sep + fileName # 新文件绝对路径
    newFile = open(newFileName, "wb") # 文件IO
    newFile.write(open(file, "rb").read())
    newFile.close()

此方法比较复杂,因为python没有像java一样强大的字符串操作

以复制文件夹D:\szcPython到文件夹D:\test为例,此方法三个入参destination是D:\test,file是D:\szcPython里的一个文件的绝对路径,这一项从队列里取,比如值为D:\szcPython\dir\szc.txt,oldDir则为D:\szcPython

经过一系列合法性判断后,先通过切片,获取file的父文件夹在源文件夹里的相对路径,存在subDir里,比如值为dir

然后subDir和目的文件夹绝对路径拼接,形成新的父文件夹绝对路径,存在newDir里,比如值为D:\test\dir

然后创建文件夹,并且在通过切片的方式获取file的文件名,比如值为szc.txt

然后文件名和newDir拼接,得到新文件的绝对路径,存在newFileName里,比如值为D:\test\dir\szc.txt

之后就是文件IO,完成复制

 

而后,就可以构造进程池,不断从队列里取出待复制的文件绝对路径,调用任务方法去执行了

    pool = multiprocessing.Pool(processes=3) # 最大容量为3的进程池

    while not q.empty(): # 队列不空,一直取元素,即待复制文件的绝对路径
        pool.apply_async(func=copyFile, args=(newDirName, q.get(), dirName, )) # 布置任务,args必须传参数入元组,并且最后一个参数必须写上,
    pool.close() # 先close,再join。等待进程池执行结束再结束主进程
    pool.join()

    while not q.empty(): # 主进程阻塞在此,直到队列为空
        pass
    print("复制完毕")

之后,我们就可以启动程序了。再次之前,为了避免入口函数的重复执行,以及确定子进程能执行任务方法,需要改变main()方法的调用方式,并且在main()方法里,加入一句话

def main():
    multiprocessing.freeze_support() # 保证多进程的执行
    ...

if __name__ == "__main__": # 主进程才会执行main()
    main()

最后,整个文件的代码如下

import os, multiprocessing

q = None

def getFilesCount(dirName):
    if dirName == None or not os.path.isdir(dirName):
        return

    files = os.listdir(dirName)
    for item in files:
        absolutePath = dirName + os.sep + item
        if os.path.isdir(absolutePath):
            getFilesCount(absolutePath)
        else:
            if q.full():
                print("缓存已满")
            else:
                q.put(absolutePath)


def copyFile(destination, file, oldDir):
    if destination == None or file == None:
        return
    if (not os.path.isdir(destination)) or (not os.path.exists(file)):
        return

    subDir = file[file.find(oldDir) + len(oldDir) + 1:file.rfind(os.sep)]
    newDir = destination + os.sep + subDir
    if not os.path.exists(newDir):
        os.mkdir(newDir)
    fileName = file[file.rfind(os.sep) + 1:]

    newFileName = newDir + os.sep + fileName
    newFile = open(newFileName, "wb")
    newFile.write(open(file, "rb").read())
    newFile.close()

def main():
    multiprocessing.freeze_support()

    dirName = "D:\\szcPython"
    newDirName = "D:\\test"
    if not os.path.isdir(dirName):
        print("无此文件夹")
        return
    if os.path.exists(newDirName):
        print("目标文件夹已存在")
        return
    os.mkdir(newDirName)

    global q
    q = multiprocessing.Manager().Queue()
    getFilesCount(dirName)
    pool = multiprocessing.Pool(processes=3)

    while not q.empty():
        pool.apply_async(func=copyFile, args=(newDirName, q.get(), dirName, ))
    pool.close()
    pool.join()

    while not q.empty():
        pass
    print("复制完毕")

if __name__ == "__main__":
    main()

测试

测试结果如下

Python多进程实现文件夹的复制_第1张图片

可见,文件夹的复制已经完成

结语

虽然python可以实现多进程,但进程的创建和销毁比线程的创建和销毁要消耗的资源大得多,因此,多任务我还是优先考虑多线程。

你可能感兴趣的:(python)