Редактировать загруженный файл (djangos FileField), используя сигнал pre_save
Я хочу отредактировать загруженный файл на уровне байтов (то есть поиск и удаление определенной последовательности байтов) перед сохранением.
У меня есть сигнал pre_save, настроенный следующим образом:
class Snippet(models.Model):
name = models.CharField(max_length=256, unique=True)
audio_file = models.FileField(upload_to=generate_file_name, blank=True, null=True)
@receiver(models.signals.pre_save, sender=Snippet)
def prepare_save(sender, instance, **kwargs):
if instance.audio_file:
remove_headers(instance)
Теперь у меня были проблемы с реализацией remove_headers
работать так, чтобы я мог редактировать файл, пока он еще находится в памяти, и сохранить его впоследствии. Я пробовал среди прочего следующее:
def remove_headers(instance):
byte_sequence = b'bytestoremove'
f = instance.audio_file.read()
file_in_hex = f.hex()
file_in_hex = re.sub(byte_sequence.hex(), '', file_in_hex)
x = b''
x = x.fromhex(file_in_hex)
tmp_file = TemporaryFile()
tmp_file.write(x)
tmp_file.flush()
tmp_file.seek(0)
instance.audio_file.save(instance.audio_file.name, tmp_file, save=True)
Это в первую очередь приведет к бесконечному циклу. Но это может быть смягчено, например, только вызывая remove_headers
метод на создание или так. Однако это не сработало, файл не изменился. Я также попытался заменить последнюю строку на:
instance.audio_file = File(tmp_file, name=instance.audio_file.name)
Это, однако, привело к пустому файлу для записи / сохранения. Любопытно, что при написании теста этот метод работает:
def test_header_removed(self):
snippet = mommy.make(Snippet)
snippet.audio_file.save('newname.mp3', ContentFile('contentbytestoremovecontent'))
snippet.save()
self.assertEqual(snippet.audio_file.read(), b'contentcontent')
Этот тест не проходит, несмотря на то, что файл в конце равен нулю байтов. Что мне здесь не хватает?
1 ответ
Второе решение было почти правильным. Причина, по которой файлы оказались пустыми (фактически это происходило только с большими файлами), заключалась в том, что иногда вам приходится искать начало файла после его открытия. Итак, начало remove_headers
необходимо изменить:
def remove_headers(instance):
byte_sequence = b'bytestoremove'
instance.audio_file.seek(0)
f = instance.audio_file.read()
file_in_hex = f.hex()