问题
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