DjangoAdmin отображает BinaryField в виде изображения

Я написал промежуточное ПО для перехвата запросов и просмотра их на странице администратора. Оно успешно просматривает текстовое тело запроса, но не справляется с рендерингом изображений.
admin.py

class RequestLogEntryAdmin(admin.ModelAdmin):
    fieldsets = (
        ('Request', {
            'fields': ('request_headers', 'request_size', 'request_body_'),
        })
    )
    
    def request_body_(self, obj):
        if b'Content-Type: image/png' in base64.b64decode(obj.request_body):
            return obj.image_handler(bytes(obj.request_body))
        return bytes(obj.request_body)

models.py

class RequestLogEntry(models.Model):
    # ...
    request_body = models.BinaryField(null=True)
    # ...
    
    def image_handler(self, binaryfield):
        return mark_safe(f'<img src="data:image/png;base64,{binaryfield}" width="150" height="150" />')

Перед сохранением request.body кодируется base64
middleware.py

    # ...
    log_entry.request_body = base64.b64encode(request.body)
    # ...

Текущий код выдает следующее:Broken image

Декодированное в Base64 request.body выглядит так:Raw request body. Я предполагаю, что к байтам изображения примешиваются какие-то дополнительные данные (например, заголовки 'Content-Disposition' и 'Content-Type'), но я не уверен, как я могу вырезать их из тела запроса.

Когда вы кодируете HttpRequest.body так:

base64.b64encode(request.body)

Вы фактически кодируете необработанное тело HTTP-запроса (которое является bytestring) как Base64 [wikipedia.org] закодированную строку.

HTTP - это текстовый протокол. Тело и заголовки представляются в виде последовательности символов. Django оставляет за собой эту особенность HTTP, сохраняя необработанное тело в HttpRequest.body для тех, кто хочет работать на этом низком уровне.

Но поскольку запрос содержит файлы, его следует обрабатывать следующим образом:

if request.FILES:
    file = request.FILES.get("image")
    if file:
        log_entry.request_body = file.read() #[1]

Теперь image_handler() придется немного изменить, потому что binaryfield - это сырые биты двоичных данных, а не строка Base64.

def image_handler(self, binaryfield):
    return mark_safe(f"<img src='data:image/png;base64,{base64.b64encode(binaryfield)}' width='150' height='150' />")

[1] Upon calling read() on an UploadedFile, you are essentially reading the entire data from the UploadedFile into memory. Be careful with this method: if the uploaded file is huge it can overwhelm your system.

Вернуться на верх