Я написал базовую программу для проверки дерева каталогов, содержащего много файлов jpeg (500000+) убедитесь, что они не повреждены (примерно 3-5% файлов, по-видимому, повреждены), а затем возьмите файл sha1sum (даже поврежденные) и сохраните информацию в базе данных.
Файлы jpeg, о которых идет речь, находятся в системе Windows и устанавливаются в окне linux через cifs. Они в основном размером около 4 мегабайт, хотя некоторые могут быть немного больше или меньше.
Когда я запускаю программу, она работает довольно хорошо некоторое время, а затем она падает с ошибкой ниже. Это было после обработки около 1100 файлов (ошибка показала, что проблема возникла при попытке открыть файл размером 4,5 мегабайта).
Теперь я понимаю, что я могу уловить эту ошибку и продолжить или повторить попытку и т.д., но мне любопытно, почему это происходит в первую очередь, и если ловить и повторить попытку на самом деле собирается решить проблему - или она просто получит (если я не ограничиваю повторные попытки, но затем файл пропущен).
Я использую "Python 2.7.5+" для дебианской системы для ее запуска. Система имеет как минимум 4 гигабайта (возможно, 8) бара, а верхняя часть сообщает, что script использует менее 1% бара и менее 3% процессора в любое время, когда он работает. Аналогично, jpeginfo, который запускает этот script, также использует одинаково небольшие объемы памяти и процессора.
Чтобы избежать слишком большого объема памяти при чтении файлов, я принял подход, приведенный в этом ответе, к другому вопросу: qaru.site/info/32672/...
Также вы можете заметить, что команда jpeginfo находится в цикле while, который ищет ответ "[OK]". Это связано с тем, что если "jpeginfo" считает, что он не может найти файл, он возвращает 0, и поэтому он не считается состоянием ошибки вызовом subprocess.check_output.
Я действительно задавался вопросом, может ли связать jpeginfo с некоторыми файлами с первой попыткой (и я подозреваю, что это так), но возвращенная ошибка говорит, что не может выделить память, а не файл не найден.
Ошибка:
Traceback (most recent call last):
File "/home/m3z/jpeg_tester", line 95, in <module>
main()
File "/home/m3z/jpeg_tester", line 32, in __init__
self.recurse(self.args.dir, self.scan)
File "/home/m3z/jpeg_tester", line 87, in recurse
cmd(os.path.join(root, name))
File "/home/m3z/jpeg_tester", line 69, in scan
with open(filepath) as f:
IOError: [Errno 12] Cannot allocate memory: '/path/to/file name.jpg'
Полный программный код:
1 #!/usr/bin/env python
2
3 import os
4 import time
5 import subprocess
6 import argparse
7 import hashlib
8 import oursql as sql
9
10
11
12 class main:
13 def __init__(self):
14 parser = argparse.ArgumentParser(description='Check jpeg files in a given directory for errors')
15 parser.add_argument('dir',action='store', help="absolute path to the directory to check")
16 parser.add_argument('-r, --recurse', dest="recurse", action='store_true', help="should we check subdirectories")
17 parser.add_argument('-s, --scan', dest="scan", action='store_true', help="initiate scan?")
18 parser.add_argument('-i, --index', dest="index", action='store_true', help="should we index the files?")
19
20 self.args = parser.parse_args()
21 self.results = []
22
23 if not self.args.dir.startswith("/"):
24 print "dir must be absolute"
25 quit()
26
27 if self.args.index:
28 self.db = sql.connect(host="localhost",user="...",passwd="...",db="fileindex")
29 self.cursor = self.db.cursor()
30
31 if self.args.recurse:
32 self.recurse(self.args.dir, self.scan)
33 else:
34 self.scan(self.args.dir)
35
36 if self.db:
37 self.db.close()
38
39 for line in self.results:
40 print line
41
42
43
44 def scan(self, dirpath):
45 print "Scanning %s" % (dirpath)
46 filelist = os.listdir(dirpath)
47 filelist.sort()
48 total = len(filelist)
49 index = 0
50 for filen in filelist:
51 if filen.lower().endswith(".jpg") or filen.lower().endswith(".jpeg"):
52 filepath = os.path.join(dirpath, filen)
53 index = index+1
54 if self.args.scan:
55 try:
56 procresult = subprocess.check_output(['jpeginfo','-c',filepath]).strip()
57 while "[OK]" not in procresult:
58 time.sleep(0.5)
59 print "\tRetrying %s" % (filepath)
60 procresult = subprocess.check_output(['jpeginfo','-c',filepath]).strip()
61 print "%s/%s: %s" % ('{:>5}'.format(str(index)),total,procresult)
62 except subprocess.CalledProcessError, e:
63 os.renames(filepath, os.path.join(dirpath, "dodgy",filen))
64 filepath = os.path.join(dirpath, "dodgy", filen)
65 self.results.append("Trouble with: %s" % (filepath))
66 print "%s/%s: %s" % ('{:>5}'.format(str(index)),total,e.output.strip())
67 if self.args.index:
68 sha1 = hashlib.sha1()
69 with open(filepath) as f:
70 while True:
71 data = f.read(8192)
72 if not data:
73 break
74 sha1.update(data)
75 sqlcmd = ("INSERT INTO `index` (`sha1`,`path`,`filename`) VALUES (?, ?, ?);", (buffer(sha1.digest()), dirpath, filen))
76 self.cursor.execute(*sqlcmd)
77
78
79 def recurse(self, dirpath, cmd, on_files=False):
80 for root, dirs, files in os.walk(dirpath):
81 if on_files:
82 for name in files:
83 cmd(os.path.join(root, name))
84 else:
85 cmd(root)
86 for name in dirs:
87 cmd(os.path.join(root, name))
88
89
90
91
92
93
94 if __name__ == "__main__":
95 main()