Edit uploaded file (djangos FileField) using pre_save signal

筅森魡賤 提交于 2021-02-08 07:56:37

问题


I want to edit an uploaded file on byte level (i.e. searching and removing a certain byte sequence) before saving it.

I have a pre_save signal set up in the following way:

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)

Now I have had problems implementing the remove_headers function in a way that I can edit the file while it is still in memory and have it stored afterwards. I tried among others the following:

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)

This first of all would result in an infinite loop. But this can be mitigated by e.g. only calling the remove_headers method on create or so. It did however not work, the file was unchanged. I also tried replacing the last line with:

instance.audio_file = File(tmp_file, name=instance.audio_file.name)

This however resulted in an empty file to be written/saved. Curiously when writing a test, this method seems to work:

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')

This test does not fail, despite the file being zero bytes in the end. What am I missing here?


回答1:


The second solution was almost correct. The reason the files ended up being empty (actually this only happened to bigger files) was, that sometimes you have to seek to the beginning of the file after opening it. So the beginngni of remove_headers needs to be changed:

def remove_headers(instance):
    byte_sequence = b'bytestoremove'
    instance.audio_file.seek(0)
    f = instance.audio_file.read()
    file_in_hex = f.hex()


来源:https://stackoverflow.com/questions/50472164/edit-uploaded-file-djangos-filefield-using-pre-save-signal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!