版本一:使用shutil进行拷贝
1 # -*- coding: utf-8 -*-
2 # @author: Tele
3 # @Time : 2019/04/02 下午 3:09
4 # 待改进:
5 # 1.拷贝逻辑使用原生的io
6 # 2.针对大文件在进程内部实现多线程方式进行拷贝
7
8
9 import time
10 import re
11 import os
12 import shutil
13 import multiprocessing
14
15
16 # 遍历文件夹
17 def walk_file(file):
18 file_list = list()
19 for root, dirs, files in os.walk(file):
20 # 遍历文件
21 for f in files:
22 file_list.append(f)
23 return file_list
24
25
26 # 计算文件数量
27 def get_file_count(dir):
28 return len(walk_file(dir))
29
30
31 def copy(src, target, queue):
32 target_number = 1
33 if os.path.isdir(src):
34 target_number = get_file_count(src)
35 shutil.copytree(src, target)
36 else:
37 shutil.copyfile(src, target)
38 # 将拷贝完成的文件数量放入队列中
39 queue.put(target_number)
40
41
42 def copy_dir(src, desc):
43 total_number = get_file_count(src)
44 # 分隔符检测
45 src = check_separator(src)
46 desc = check_separator(desc)
47 # print("src:",src)
48 # print("desc:",desc)
49
50 file_dir_list = [src + "/" + i for i in os.listdir(src)]
51 if os.path.exists(desc):
52 shutil.rmtree(desc)
53 pool = multiprocessing.Pool(3)
54
55 # 创建队列
56 queue = multiprocessing.Manager().Queue()
57
58 # 一个文件/目录开启一个进程去拷贝
59 for f_name in file_dir_list:
60 target = desc + "/" + f_name[index_list("/", f_name)[1] + 1:]
61 # print(target)
62 # 创建target目录
63 parent_path = os.path.split(target)[0]
64 if not os.path.exists(parent_path):
65 os.makedirs(parent_path)
66 pool.apply_async(copy, args=(f_name, target, queue,))
67
68 start = time.time()
69 pool.close()
70 # pool.join()
71 count = 0
72 while True:
73 count += queue.get()
74 # 格式化输出时两个%输出一个%,不换行,每次定位到行首,实现覆盖
75 print("\r拷贝进度为 %.2f %%" % (count * 100 / total_number), end="")
76 if count >= total_number:
77 break
78 end = time.time()
79 print()
80 print("耗时-----", (end - start), "s")
81
82
83 # 查找指定字符出现的全部索引位置
84 def index_list(c, s):
85 return [i.start() for i in re.finditer(c, s)]
86
87
88 # 检测目录结尾是否有 "/"
89 def check_separator(path):
90 if path.rindex("/") == len(path) - 1:
91 return path[0:path.rindex("/")]
92 return path
93
94
95 def main():
96 copy_dir("f:/ftp_mypc/", "e:/ftp_mypc/")
97
98
99 if __name__ == '__main__':
100 main()
这样做仍然有些小问题,对于大文件可以在进程内部采用多线程的方式,可以看到使用shutil进行拷贝时我们没有办法实现字节切割,于是有了下面的版本二
版本二:
1 # -*- coding: utf-8 -*-
2 # @author: Tele
3 # @Time : 2019/04/02 下午 3:09
4 # 使用多进程拷贝文件夹,对于大文件进程内部又使用了多线程进行拷贝
5 # 使用进程池实现多进程时,使用的消息队列要使用multiprocessing.Manager().Queue()创建
6
7 import time
8 import re
9 import os
10 import shutil
11 import multiprocessing
12 import math
13 from concurrent.futures import ThreadPoolExecutor, wait
14
15 # 设置单个文件的最大值:209715200 200M
16 MAX_SINGLE_FILE_SIZE = 209715200
17 mutex = multiprocessing.Lock()
18 executor = ThreadPoolExecutor(max_workers=3)
19
20
21 # 遍历文件夹
22 def walk_file(file):
23 file_list = list()
24 for root, dirs, files in os.walk(file):
25 # 遍历文件
26 for f in files:
27 file_list.append(f)
28
29 # 空文件夹处理
30 for d in dirs:
31 if len(os.listdir(os.path.join(root, d))) == 0:
32 file_list.append(d)
33 return file_list
34
35
36 # 计算文件数量
37 def get_file_count(dir):
38 return len(walk_file(dir))
39
40
41 def copy(src, target, queue):
42 target_number = 1
43 buffer = 1024
44 # 文件夹
45 if os.path.isdir(src):
46 target_number = get_file_count(src)
47 for root, dirs, files in os.walk(src):
48 # 遍历文件
49 for f in files:
50 drive = os.path.splitdrive(target)[0]
51 target = drive + os.path.splitdrive(os.path.join(root, f))[1]
52 copy_single_file(buffer, os.path.join(root, f), target)
53 # 空文件夹
54 for d in dirs:
55 drive = os.path.splitdrive(target)[0]
56 target = drive + os.path.splitdrive(os.path.join(root, d))[1]
57 # 检查文件的层级目录
58 if not os.path.exists(target):
59 os.makedirs(target)
60 else:
61 copy_single_file(buffer, src, target)
62 # 将拷贝完成的文件数量放入队列中
63 queue.put(target_number)
64
65
66 # 拷贝单文件
67 def copy_single_file(buffer, src, target):
68 file_size = os.path.getsize(src)
69 rs = open(src, "rb")
70
71 # 检查文件的层级目录
72 parent_path = os.path.split(target)[0]
73 if not os.path.exists(parent_path):
74 os.makedirs(parent_path)
75
76 ws = open(target, "wb")
77 # 小文件直接读写
78 if file_size <= MAX_SINGLE_FILE_SIZE:
79 while True:
80 content = rs.read(buffer)
81 ws.write(content)
82 if len(content) == 0:
83 break
84 ws.flush()
85 else:
86 # 设置每个线程拷贝的字节数 50M
87 PER_THREAD_SIZE = 52428800
88 # 构造参数并执行
89 task_list = list()
90 for i in range(math.ceil(file_size / PER_THREAD_SIZE)):
91 byte_size = PER_THREAD_SIZE
92 # 最后一个线程拷贝的字节数应该是取模
93 if i == math.ceil(file_size / PER_THREAD_SIZE) - 1:
94 byte_size = file_size % PER_THREAD_SIZE
95 start = i * PER_THREAD_SIZE + i
96 t = executor.submit(copy_file_thread, start, byte_size, rs, ws)
97 task_list.append(t)
98 wait(task_list)
99 if rs:
100 rs.close()
101 if ws:
102 ws.close()
103
104
105 # 多线程拷贝
106 def copy_file_thread(start, byte_size, rs, ws):
107 mutex.acquire()
108 buffer = 1024
109 count = 0
110 rs.seek(start)
111 ws.seek(start)
112 while True:
113 if count + buffer <= byte_size:
114 content = rs.read(buffer)
115 count += len(content)
116 write(content, ws)
117 else:
118 content = rs.read(byte_size % buffer)
119 count += len(content)
120 write(content, ws)
121 break
122 # global total_count
123 # total_count += byte_size
124 # print("\r拷贝进度为%.2f %%" % (total_count * 100 / file_size), end="")
125 mutex.release()
126
127
128 def write(content, ws):
129 ws.write(content)
130 ws.flush()
131
132
133 def copy_dir(src, desc):
134 # 获得待拷贝的文件总数(含空文件夹)
135 total_number = get_file_count(src)
136 # 分隔符检测
137 src = check_separator(src)
138 desc = check_separator(desc)
139 # print("src:",src)
140 # print("desc:",desc)
141
142 file_dir_list = [src + "/" + i for i in os.listdir(src)]
143 if os.path.exists(desc):
144 shutil.rmtree(desc)
145
146 # 进程池
147 pool = multiprocessing.Pool(3)
148
149 # 创建队列
150 queue = multiprocessing.Manager().Queue()
151
152 # 一个文件/目录开启一个进程去拷贝
153 for f_name in file_dir_list:
154 target = os.path.splitdrive(desc)[0] + "/" + os.path.splitdrive(f_name)[1]
155 # target = desc + "/" + f_name[index_list("/", f_name)[1] + 1:]
156 # print(target)
157 # 创建target目录
158 parent_path = os.path.split(target)[0]
159 if not os.path.exists(parent_path):
160 os.makedirs(parent_path)
161 pool.apply_async(copy, args=(f_name, target, queue))
162
163 start = time.time()
164 pool.close()
165 # pool.join()
166 count = 0
167 while True:
168 count += queue.get()
169 # 格式化输出时两个%输出一个%,不换行,每次定位到行首,实现覆盖
170 print("\r当前进度为 %.2f %%" % (count * 100 / total_number), end="")
171 if count >= total_number:
172 break
173
174 executor.shutdown()
175 end = time.time()
176 print()
177 print("耗时-----", (end - start), "s")
178
179
180 # 查找指定字符出现的全部索引位置
181 def index_list(c, s):
182 return [i.start() for i in re.finditer(c, s)]
183
184
185 # 检测目录结尾是否有 "/"
186 def check_separator(path):
187 if path.rindex("/") == len(path) - 1:
188 return path[0:path.rindex("/")]
189 return path
190
191
192 def main():
193 copy_dir("f:/ftp_mypc/", "e:/ftp_mypc/")
194
195
196 if __name__ == '__main__':
197 main()