Django自定义存储类实现文件存前加密、取后解密功能失效排查求助
问题分析与解决方案
我来帮你梳理下代码里的问题,核心是文件指针的处理错误,导致加密后的内容根本没被正确保存,另外_open方法也有个小问题需要修正。
为什么当前代码不工作?
文件指针位置错误:
当你调用content.file.read()读取原文件内容后,文件对象的指针已经移动到了文件末尾。这时候你执行content.file.write(encrypted),其实是把加密内容追加到了原文件的后面,而不是覆盖原内容。
之后你调用content.file.read()时,指针依然停在末尾,读取到的是空字节,所以打印结果是False。父类的_save方法执行时,也是从当前指针位置开始读取,自然读不到加密后的内容,最终保存的还是原文件。_open方法的类文件对象问题:
原_open方法直接把解密后的字节串传给File类,这是错误的——File需要接收一个实现了文件接口的对象(比如带read/write方法的类文件对象),直接传字节串会引发异常。
修正后的完整代码
import hashlib import os import uuid from io import BytesIO import django.core.files.storage as storage from cryptography.fernet import Fernet from django.conf import settings from django.core.files import File class DefaultStorage(storage.FileSystemStorage): def __init__(self): super(DefaultStorage, self).__init__() self.encryptor = Fernet(settings.ENCRYPTION_KEY) def _save(self, name, content): # 1. 读取原文件内容 original_content = content.file.read() # 2. 加密内容 encrypted_content = self.encryptor.encrypt(original_content) # 3. 将指针移回文件开头,清空原文件内容 content.file.seek(0) content.file.truncate() # 4. 写入加密后的内容 content.file.write(encrypted_content) # 5. 再次将指针移回开头,确保父类方法能读取到完整加密内容 content.file.seek(0) # 验证:现在读取的内容应该等于加密后的内容 print(content.file.read() == encrypted_content) # 验证后把指针移回开头,不影响后续保存 content.file.seek(0) return super(DefaultStorage, self)._save(name, content) def _open(self, name, mode='rb'): # 读取加密后的文件内容 with open(self.path(name), mode) as f: encrypted_content = f.read() # 解密后用BytesIO包装成类文件对象 decrypted_content = self.encryptor.decrypt(encrypted_content) return File(BytesIO(decrypted_content)) def get_available_name(self, name, max_length=None): dir_name, file_name = os.path.split(name) file_root, file_ext = os.path.splitext(file_name) file_root = hashlib.md5(file_root.encode()).hexdigest() name = os.path.join(dir_name, file_root + file_ext) return super(DefaultStorage, self).get_available_name(name, max_length) def get_alternative_name(self, file_root, file_ext): return '%s%s%s' % (file_root, uuid.uuid4().hex, file_ext)
关键修改点说明
- _save方法:
- 读取原内容后,用
seek(0)回到文件开头,truncate()清空原文件,确保加密内容是覆盖写入而非追加。 - 写入加密内容后再次
seek(0),保证父类的_save方法能从开头读取到完整的加密内容。
- 读取原内容后,用
- _open方法:
- 使用
BytesIO把解密后的字节串包装成类文件对象,再传给File类,符合Django对文件对象的要求。
- 使用
这样修改后,文件在保存前会被正确加密,读取时自动解密,就能实现你想要的功能了。
内容的提问来源于stack exchange,提问作者Aryan Iyappan
相关产品推荐
相关产品推荐

