diff --git a/README.md b/README.md index 63ace0e..04d91ee 100644 --- a/README.md +++ b/README.md @@ -1 +1,105 @@ -Hello word +# Вычисление SHA1 хэша из Django ImageField + +Этот репозиторий содержит готовые решения для вычисления SHA1 хэша содержимого файлов изображений из Django ImageField. + +## Файлы + +- **`image_sha1_hash.py`** - основной модуль с функциями для вычисления SHA1 хэша +- **`example_usage.py`** - примеры использования в различных сценариях Django + +## Основные функции + +### `get_image_file_sha1(image_field)` + +Вычисляет SHA1 хэш содержимого файла изображения из Django ImageField. + +```python +from image_sha1_hash import get_image_file_sha1 +from myapp.models import Photo + +photo = Photo.objects.get(id=1) +sha1_hash = get_image_file_sha1(photo.image) +print(sha1_hash) # Выведет: 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3' +``` + +### `get_image_file_sha1_alternative(image_field)` + +Альтернативный метод через прямое чтение файла (полезен, если метод `chunks()` недоступен). + +## Быстрый старт + +1. Скопируйте `image_sha1_hash.py` в ваш Django проект +2. Импортируйте функцию в нужном месте: + +```python +from image_sha1_hash import get_image_file_sha1 +``` + +3. Используйте с вашими моделями, которые содержат ImageField + +## Примеры использования + +### 1. Автоматическое вычисление при сохранении модели + +```python +from django.db import models +from image_sha1_hash import get_image_file_sha1 + +class Photo(models.Model): + image = models.ImageField(upload_to='photos/') + image_sha1 = models.CharField(max_length=40, blank=True, editable=False) + + def save(self, *args, **kwargs): + if self.image: + self.image_sha1 = get_image_file_sha1(self.image) + super().save(*args, **kwargs) +``` + +### 2. Использование в Django View + +```python +from django.shortcuts import get_object_or_404 +from django.http import JsonResponse + +def photo_hash_api(request, photo_id): + photo = get_object_or_404(Photo, id=photo_id) + sha1_hash = get_image_file_sha1(photo.image) + + return JsonResponse({ + 'photo_id': photo.id, + 'sha1_hash': sha1_hash + }) +``` + +### 3. Поиск дубликатов изображений + +```python +def find_duplicate_images(): + duplicates = {} + for photo in Photo.objects.filter(image__isnull=False): + sha1 = get_image_file_sha1(photo.image) + if sha1 in duplicates: + duplicates[sha1].append(photo) + else: + duplicates[sha1] = [photo] + return {k: v for k, v in duplicates.items() if len(v) > 1} +``` + +Больше примеров смотрите в файле `example_usage.py`. + +## Особенности + +- ✅ Эффективная работа с большими файлами (чтение по частям) +- ✅ Обработка исключений +- ✅ Два варианта реализации (через chunks() и через прямое чтение) +- ✅ Совместимость с Django ImageField и FileField +- ✅ Примеры для различных сценариев (Views, Signals, Celery, DRF) + +## Требования + +- Python 3.6+ +- Django 2.0+ + +## Лицензия + +MIT diff --git a/example_usage.py b/example_usage.py new file mode 100644 index 0000000..ad7392a --- /dev/null +++ b/example_usage.py @@ -0,0 +1,259 @@ +""" +Примеры использования функций для вычисления SHA1 хэша из Django ImageField. +""" + +# ============================================================================ +# ПРИМЕР 1: Простое использование в Django shell или view +# ============================================================================ + +""" +from image_sha1_hash import get_image_file_sha1 +from myapp.models import Photo + +# Получить объект с изображением +photo = Photo.objects.get(id=1) + +# Вычислить SHA1 хэш +sha1_hash = get_image_file_sha1(photo.image) +print(f"SHA1: {sha1_hash}") +""" + + +# ============================================================================ +# ПРИМЕР 2: Django модель с автоматическим вычислением SHA1 при сохранении +# ============================================================================ + +""" +# В models.py: + +from django.db import models +from image_sha1_hash import get_image_file_sha1 + +class Photo(models.Model): + title = models.CharField(max_length=200) + image = models.ImageField(upload_to='photos/') + image_sha1 = models.CharField(max_length=40, blank=True, editable=False) + created_at = models.DateTimeField(auto_now_add=True) + + def save(self, *args, **kwargs): + # Автоматически вычисляем SHA1 при сохранении + if self.image: + self.image_sha1 = get_image_file_sha1(self.image) + super().save(*args, **kwargs) + + def __str__(self): + return self.title +""" + + +# ============================================================================ +# ПРИМЕР 3: Использование в Django View +# ============================================================================ + +""" +# В views.py: + +from django.shortcuts import render, get_object_or_404 +from django.http import JsonResponse +from myapp.models import Photo +from image_sha1_hash import get_image_file_sha1 + +def photo_detail(request, photo_id): + photo = get_object_or_404(Photo, id=photo_id) + sha1_hash = get_image_file_sha1(photo.image) + + context = { + 'photo': photo, + 'sha1_hash': sha1_hash + } + return render(request, 'photo_detail.html', context) + +def photo_hash_api(request, photo_id): + '''API endpoint для получения SHA1 хэша изображения''' + photo = get_object_or_404(Photo, id=photo_id) + sha1_hash = get_image_file_sha1(photo.image) + + return JsonResponse({ + 'photo_id': photo.id, + 'title': photo.title, + 'sha1_hash': sha1_hash + }) +""" + + +# ============================================================================ +# ПРИМЕР 4: Проверка дубликатов изображений +# ============================================================================ + +""" +# В utils.py: + +from django.db.models import Count +from myapp.models import Photo +from image_sha1_hash import get_image_file_sha1 + +def find_duplicate_images(): + '''Находит дубликаты изображений по SHA1 хэшу''' + duplicates = {} + + for photo in Photo.objects.filter(image__isnull=False): + sha1 = get_image_file_sha1(photo.image) + if sha1: + if sha1 in duplicates: + duplicates[sha1].append(photo) + else: + duplicates[sha1] = [photo] + + # Возвращаем только хэши с дубликатами + return {k: v for k, v in duplicates.items() if len(v) > 1} + +def update_all_sha1_hashes(): + '''Обновляет SHA1 хэши для всех фотографий''' + photos = Photo.objects.filter(image__isnull=False) + updated = 0 + + for photo in photos: + sha1 = get_image_file_sha1(photo.image) + if sha1: + photo.image_sha1 = sha1 + photo.save(update_fields=['image_sha1']) + updated += 1 + + return updated +""" + + +# ============================================================================ +# ПРИМЕР 5: Django Management Command +# ============================================================================ + +""" +# В management/commands/calculate_image_hashes.py: + +from django.core.management.base import BaseCommand +from myapp.models import Photo +from image_sha1_hash import get_image_file_sha1 + +class Command(BaseCommand): + help = 'Вычисляет SHA1 хэши для всех изображений' + + def add_arguments(self, parser): + parser.add_argument( + '--force', + action='store_true', + help='Перезаписать существующие хэши', + ) + + def handle(self, *args, **options): + force = options['force'] + + if force: + photos = Photo.objects.filter(image__isnull=False) + else: + photos = Photo.objects.filter(image__isnull=False, image_sha1='') + + total = photos.count() + updated = 0 + + self.stdout.write(f'Обработка {total} изображений...') + + for photo in photos: + sha1 = get_image_file_sha1(photo.image) + if sha1: + photo.image_sha1 = sha1 + photo.save(update_fields=['image_sha1']) + updated += 1 + + self.stdout.write( + self.style.SUCCESS(f'Успешно обновлено {updated} хэшей') + ) + +# Использование: +# python manage.py calculate_image_hashes +# python manage.py calculate_image_hashes --force +""" + + +# ============================================================================ +# ПРИМЕР 6: Django REST Framework Serializer +# ============================================================================ + +""" +# В serializers.py: + +from rest_framework import serializers +from myapp.models import Photo +from image_sha1_hash import get_image_file_sha1 + +class PhotoSerializer(serializers.ModelSerializer): + image_sha1_hash = serializers.SerializerMethodField() + + class Meta: + model = Photo + fields = ['id', 'title', 'image', 'image_sha1_hash', 'created_at'] + + def get_image_sha1_hash(self, obj): + '''Вычисляет SHA1 хэш изображения для API''' + if obj.image: + return get_image_file_sha1(obj.image) + return None +""" + + +# ============================================================================ +# ПРИМЕР 7: Использование с Django Signals +# ============================================================================ + +""" +# В signals.py: + +from django.db.models.signals import pre_save +from django.dispatch import receiver +from myapp.models import Photo +from image_sha1_hash import get_image_file_sha1 + +@receiver(pre_save, sender=Photo) +def calculate_image_sha1(sender, instance, **kwargs): + '''Автоматически вычисляет SHA1 хэш при сохранении''' + if instance.image: + instance.image_sha1 = get_image_file_sha1(instance.image) + +# Не забудьте импортировать signals в apps.py: +# В apps.py: +class MyappConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'myapp' + + def ready(self): + import myapp.signals +""" + + +# ============================================================================ +# ПРИМЕР 8: Асинхронное вычисление с Celery +# ============================================================================ + +""" +# В tasks.py: + +from celery import shared_task +from myapp.models import Photo +from image_sha1_hash import get_image_file_sha1 + +@shared_task +def calculate_photo_sha1(photo_id): + '''Celery задача для вычисления SHA1 хэша''' + try: + photo = Photo.objects.get(id=photo_id) + sha1 = get_image_file_sha1(photo.image) + if sha1: + photo.image_sha1 = sha1 + photo.save(update_fields=['image_sha1']) + return f'SHA1 для фото {photo_id}: {sha1}' + except Photo.DoesNotExist: + return f'Фото {photo_id} не найдено' + +# Использование: +# from myapp.tasks import calculate_photo_sha1 +# calculate_photo_sha1.delay(photo_id=1) +""" diff --git a/image_sha1_hash.py b/image_sha1_hash.py new file mode 100644 index 0000000..e4379a1 --- /dev/null +++ b/image_sha1_hash.py @@ -0,0 +1,134 @@ +""" +Модуль для вычисления SHA1 хэша содержимого файла изображения из Django ImageField. +""" +import hashlib +from typing import Optional + + +def get_image_file_sha1(image_field) -> Optional[str]: + """ + Вычисляет SHA1 хэш содержимого файла изображения из Django ImageField. + + Args: + image_field: Django ImageField объект (например, model_instance.image) + + Returns: + str: SHA1 хэш в шестнадцатеричном формате или None, если файл не существует + + Example: + >>> from myapp.models import MyModel + >>> instance = MyModel.objects.get(id=1) + >>> sha1_hash = get_image_file_sha1(instance.image) + >>> print(sha1_hash) + 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3' + """ + if not image_field: + return None + + try: + # Открываем файл и вычисляем SHA1 хэш + sha1 = hashlib.sha1() + + # Открываем файл в бинарном режиме + image_field.open('rb') + + # Читаем файл по частям для эффективной работы с большими файлами + for chunk in image_field.chunks(chunk_size=8192): + sha1.update(chunk) + + # Закрываем файл + image_field.close() + + # Возвращаем хэш в шестнадцатеричном формате + return sha1.hexdigest() + + except Exception as e: + print(f"Ошибка при вычислении SHA1: {e}") + return None + + +def get_image_file_sha1_alternative(image_field) -> Optional[str]: + """ + Альтернативный метод вычисления SHA1 хэша через прямое чтение файла. + Полезен, если метод chunks() недоступен. + + Args: + image_field: Django ImageField объект + + Returns: + str: SHA1 хэш в шестнадцатеричном формате или None + """ + if not image_field: + return None + + try: + sha1 = hashlib.sha1() + + # Используем path для прямого доступа к файлу + with open(image_field.path, 'rb') as f: + while True: + data = f.read(8192) + if not data: + break + sha1.update(data) + + return sha1.hexdigest() + + except Exception as e: + print(f"Ошибка при вычислении SHA1: {e}") + return None + + +def add_sha1_to_model_method(model_class): + """ + Декоратор или функция для добавления метода get_image_sha1 в модель Django. + + Пример использования в models.py: + + class MyModel(models.Model): + image = models.ImageField(upload_to='images/') + + def get_image_sha1(self): + '''Возвращает SHA1 хэш файла изображения''' + return get_image_file_sha1(self.image) + """ + pass + + +# Пример использования в Django модели +""" +# В models.py: + +from django.db import models +from image_sha1_hash import get_image_file_sha1 + +class Photo(models.Model): + title = models.CharField(max_length=200) + image = models.ImageField(upload_to='photos/') + image_sha1 = models.CharField(max_length=40, blank=True, editable=False) + + def save(self, *args, **kwargs): + # Автоматически вычисляем SHA1 при сохранении + if self.image: + self.image_sha1 = get_image_file_sha1(self.image) + super().save(*args, **kwargs) + + def get_image_hash(self): + '''Метод для получения SHA1 хэша изображения''' + return get_image_file_sha1(self.image) + + +# В views.py или в Django shell: + +from myapp.models import Photo + +# Получить SHA1 хэш существующего изображения +photo = Photo.objects.get(id=1) +sha1_hash = photo.get_image_hash() +print(f"SHA1 хэш изображения: {sha1_hash}") + +# Или напрямую: +from image_sha1_hash import get_image_file_sha1 +sha1_hash = get_image_file_sha1(photo.image) +print(f"SHA1 хэш: {sha1_hash}") +"""