Я написал базовую программу для проверки дерева каталогов, содержащего много файлов 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()
