小脚本之检测超规文件

Python脚本,检测不符合规则的文件


起源

热更工作流的一个问题。
美术会交付策划未压缩的图片/视频,策划不检查直接提交,在审核更新资源的时候发现不合规资源,打回美术/策划压缩调整。

其他问题:

  • 检查不合规文件,需要开发遍历每条提交记录的文件,并判断是否合规;不同用途的文件是否合规的标准不同
  • 现有环境下存在一些不合规的文件(之前开发疏忽,已更新到线上环境)




方案

不合规文件特点:

  • 不同用途(文件夹下)的文件,规范不同
  • 可以通用的根据大小来判断合规性
  • 部分不合规的文件需要过滤掉(已经在线上了,不需要列出来)

最终的设计思路:

  1. 遍历所有文件,将超过阈值的文件全部列出
  2. 阈值可以动态调整
    • 所属文件夹
    • 具体文件名
    • 文件后缀
  3. 可以设定缓存文件,若文件在缓存中,可以被过滤,不会列出

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#coding:utf-8

######################################################
#
# File: 检查文件大小
# Version: v0.0.1
#
#


import os
from os.path import join, getsize, isdir, splitext
import json
import codecs


##################################################
##
## 一些配置项

# 1KB = 1024
# 1MB = 1024 * 1024 = 1048576
# 5MB = 5 * 1024 * 1024

# 默认大小(byte, 1KB = 1024b)
FILE_DEFAULT_SIZE = 1048576

# 特殊大小限制(以文件夹为单位)
SPECIAL_FOLDER_SIZE_MAP = {
# 'folder path(without \\ suffix)' : size,
'.\\Assets\\asset\\bg' : 2097152, #2MB
}

# 特殊大小限制(以文件为单位)
SPECIAL_FILE_SIZE_MAP = {
# 'file path' : size,

}

# 忽略的文件后缀
IGNORE_FILE_SUFFIX_LIST = [
# '.suffix',
'.exe',
'.dll',
'.animxml',
'.ttf',
]

# 原始路径
ORIGINAL_PATH = ".\\Assets\\"

# 缓存文件
STORAGE_FILE_PATH = ".\\checkFileSizeTemp.json"

# 是否生成缓存文件
IS_GENERATE_STORAGE_FILE = False

def needCheckFile(file_path, storage_map):
""" Check is file need to calculate size
1. is file suffix in IGNORE_FILE_SUFFIX_LIST

file_path: string
file path(include suffix)
storage_map: map<file_path, file info>
need be ignore file path

returns:
boolean: is file need be checked
"""

file_cfg = splitext(file_path)
if file_cfg[1] in IGNORE_FILE_SUFFIX_LIST:
return False

if file_path in storage_map.keys():
return False

return True

def getDefaultSize(file_path, folder_path):
""" Get default standard size(byte)
1. get size by SPECIAL_FILE_SIZE_MAP and file path
2. get size by SPECIAL_FOLDER_SIZE_MAP and belong folder
3. get size by FILE_DEFAULT_SIZE direc

file_path: string
file path(include suffix)
folder_path: string
folder_path

returns:
int: file standrd size(byte)
"""

size = SPECIAL_FILE_SIZE_MAP.get(file_path)

if not size:
size = SPECIAL_FOLDER_SIZE_MAP.get(folder_path, FILE_DEFAULT_SIZE)

return size


def notFileSizeStandard(file_path, folder_path):
""" Check is file size standard
compare file size and file standard size

file_path: string
file path(include suffix)
folder_path: string
folder_path

returns:
boolean: is file size not standard
"""

default_size = getDefaultSize(file_path, folder_path)
file_size = getsize(file_path)

# print(file_path + ' , ' + str(file_size) + ', ' + str(default_size) + ' == ' + str(file_size >= default_size))
return file_size >= default_size, file_size, default_size

def walkFolders(folder_path, storage_map):
""" Check is file size standard
compare file size and file standard size

folder_path: string
folder_path
storage_map: map<file path, file info>
need be ignore file path

returns:
dict[fileInfo]: illegal file info map
dict[fileInfo]
path: file path
size: file size
standard: file standard size
"""

illegal_file_map = {}

for file_path in os.listdir(folder_path):
temp_path = join(folder_path, file_path)
if isdir(temp_path):
temp_map = walkFolders(temp_path, storage_map)
illegal_file_map.update(temp_map)
else:
if needCheckFile(temp_path, storage_map):
notStandard, file_size, standard_size = notFileSizeStandard(temp_path, folder_path)
if notStandard:
file_info = {}
file_info["path"] = temp_path
file_info["size"] = file_size
file_info["standard"] = standard_size

illegal_file_map[temp_path] = file_info


return illegal_file_map


def saveStorageFileMap(illegal_file_map, storage_file_map):
if not IS_GENERATE_STORAGE_FILE:
return

storage_file_map.update(illegal_file_map)
with codecs.open(STORAGE_FILE_PATH,'w', 'utf-8') as outf:
json.dump(storage_file_map, outf, ensure_ascii=False)
outf.write('\n')


def loadStorageFileMap():
if not os.path.isfile(STORAGE_FILE_PATH):
print("[WARNING] storage file not exist!")
return {}

file_map = {}
with codecs.open(STORAGE_FILE_PATH, "r", "utf-8") as f:
for line in f:
temp = json.loads(line)
file_map.update(temp)

return file_map


def regularOutputResult(illegal_file_map):
print("\n\n-- Result: ")
if len(illegal_file_map) <= 0:
print("\n==========> Success! <==========\n\n")
else:
print("\n==========> !Error! <==========")
print("==========> !Error! <==========")
print("==========> !Error! <==========\n")
for k, v in illegal_file_map.items():
size = v['size'] / 1024
if size / 1024 > 0:
print('\t' + k + ' | ' + str(size / 1024) + 'MB')
else:
print('\t' + k + ' | ' + str(size) + 'KB')

print("\n==========> !Error! <==========")
print("==========> !Error! <==========")
print("==========> !Error! <==========\n")



def main(dir):
print("==================================================")
print("Start")
print("-- Config: ")
print("\tOri Path: " + str(dir))
print("\tStorage Path: " + str(STORAGE_FILE_PATH))
print("\tIs Generate Storage File: " + str(IS_GENERATE_STORAGE_FILE))

storage_file_map = loadStorageFileMap()
# print(json.dumps(storage_file_map, indent=4, ensure_ascii=False, encoding='utf-8'))

illegal_file_map = walkFolders(dir, storage_file_map)
regularOutputResult(illegal_file_map)
saveStorageFileMap(illegal_file_map, storage_file_map)


if __name__ == "__main__":
main(ORIGINAL_PATH)

使用流程:
初次使用:

  1. 找到一个稳定版本,执行缓存模式,缓存当前已在线上的不合规文件
  2. 动态配置一些阈值

后续使用:

  1. 在合并完资源后,执行脚本
  2. 若存在不合规文件,则列出具体路径,通知策划调整;若无不合规文件,显示Success。




改进

依旧存在改进的方向。

按流程来说

  1. 美术原图
  2. 策划修正
  3. 开发检查

如果出现问题:

  1. 美术原图
  2. 策划修正
  3. 开发检查,不合规,反馈策划
  4. 策划再次修正
  5. 开发检查,合规

那么,既然我们开发检查的内容可以脚本化,即自动化;那完全可以前置检查,让策划无法提交不合规的文件。
这个就涉及到Hook,相关内容可参考文章 SVN之Hook