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)
# ...
Декодированное в Base64 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.