Django用户上传加密文件下载及下载后删除功能问题求助
Django文件加密网站问题修复方案
问题说明
基于Django开发的文件加密解密网站,已实现文件上传加密功能,但存在两个问题:
- 无法下载media目录中保存的加密文件
- 需要实现文件下载完成后自动删除的功能
现有代码
models.py
from django.db import models import os class Fileupload(models.Model): file = models.FileField(upload_to='media') date = models.DateTimeField(auto_now_add=True) def __str__(self): return self.file # 错误:返回FileField对象而非字符串 def filename(self): return os.path.basename(self.file.name)
views.py
from msilib.schema import File from urllib import response from django.shortcuts import render,HttpResponse from .models import Fileupload from .encryptfile import encrypt import os from cryptography.fernet import Fernet import mimetypes def base(request): if request.method == 'POST': file2 = request.FILES["file"] document = Fileupload.objects.create(file=file2) document.save() fname = Fileupload.filename(document) global path1 path1 = "media/"+fname # 加密逻辑存在错误 key = Fernet.generate_key() with open("thekey.key","wb") as thekey: thekey.write(key) with open(path1, 'rb') as file: original = file2.read() # 错误:应读取本地保存的文件而非上传的临时文件 encrypted = Fernet(key).encrypt(original) with open(path1, 'wb') as encrypted_file: encrypted_file.write(encrypted) return render(request,'download.html') else: return render(request, 'base.html') def download(request): BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # filename未定义 filepath = BASE_DIR + '/media/' + filename path1 = open(filepath, 'r') # 错误:二进制文件需用rb模式 mime_type, _ = mimetypes.guess_type(filepath) response = HttpResponse(path, content_type=mime_type) # 错误:变量名应为path1 response['Content-Disposition'] = "attachment; filename=%s" % filename return render(request, response,'base.html') # 错误:不应使用render返回文件响应 def decrypt(request): return render(request, 'decrypt.html')
urls.py
from django.contrib import admin from django.urls import path from Home import views from django.conf.urls.static import static from django.conf import settings urlpatterns = [ path('admin/', admin.site.urls), path('download',views.download,name='download'), path('decrypt',views.decrypt,name='decrypt'), path('',views.base,name='base'), ] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
download.html
<section class="u-align-center u-clearfix u-image u-shading u-section-1" src="static/img/bghack.jpg" data-image-width="7395" data-image-height="4933" id="sec-f710"> <div class="u-clearfix u-sheet u-sheet-1"> <h1 class="u-text u-text-default u-title u-text-1">ENCRYPTION OF FILES</h1> <p class="u-large-text u-text u-text-default u-text-variant u-text-2">The file has been encrypted. Download the file by clicking the button below.</p> {% if url %} <div class = "download"> <a href="{{ url }}" type="button" download class="u-btn u-button-style u-palette-2-base u-btn-1">Download the file</a> </div> {% endif %} </div> </section>
修复方案
1. 修复文件下载功能
核心修改点:
- 移除全局变量,改用session传递文件ID
- 修正文件读取模式为二进制
rb - 直接返回
HttpResponse作为文件响应 - 修正加密逻辑中读取文件的错误
修改后的代码:
models.py(修正__str__方法)
from django.db import models import os class Fileupload(models.Model): file = models.FileField(upload_to='media') date = models.DateTimeField(auto_now_add=True) def __str__(self): return self.file.name # 返回文件路径字符串 def filename(self): return os.path.basename(self.file.name)
views.py(重写base和download视图)
from django.shortcuts import render,HttpResponse from .models import Fileupload import os from cryptography.fernet import Fernet import mimetypes def base(request): if request.method == 'POST': file2 = request.FILES["file"] document = Fileupload.objects.create(file=file2) document.save() fname = document.filename() file_path = document.file.path # 修正加密逻辑:读取本地已保存的文件 key = Fernet.generate_key() with open("thekey.key","wb") as thekey: thekey.write(key) with open(file_path, 'rb') as file: original = file.read() encrypted = Fernet(key).encrypt(original) with open(file_path, 'wb') as encrypted_file: encrypted_file.write(encrypted) # 将文件ID存入session,供下载视图使用 request.session['file_id'] = document.id # 传递文件名到下载页面 return render(request,'download.html', {'file_name': fname}) else: return render(request, 'base.html') def download(request): # 从session获取文件ID file_id = request.session.get('file_id') if not file_id: return HttpResponse("无可用下载文件", status=404) try: document = Fileupload.objects.get(id=file_id) file_path = document.file.path # 二进制读取加密文件 with open(file_path, 'rb') as f: content_type = mimetypes.guess_type(file_path)[0] or 'application/octet-stream' response = HttpResponse(f.read(), content_type=content_type) response['Content-Disposition'] = f'attachment; filename="{document.filename()}"' # 下载完成后自动删除文件及数据库记录 def cleanup(response): if os.path.exists(file_path): os.remove(file_path) document.delete() del request.session['file_id'] # 清除session中的文件ID response.close = cleanup return response except Fileupload.DoesNotExist: return HttpResponse("文件不存在", status=404) def decrypt(request): return render(request, 'decrypt.html')
download.html(修改下载按钮路由)
<section class="u-align-center u-clearfix u-image u-shading u-section-1" src="static/img/bghack.jpg" data-image-width="7395" data-image-height="4933" id="sec-f710"> <div class="u-clearfix u-sheet u-sheet-1"> <h1 class="u-text u-text-default u-title u-text-1">文件已加密完成</h1> <p class="u-large-text u-text u-text-default u-text-variant u-text-2">文件已加密,点击下方按钮下载。</p> <div class="download"> <a href="{% url 'download' %}" class="u-btn u-button-style u-palette-2-base u-btn-1">下载文件</a> </div> </div> </section>
2. 实现下载完成后自动删除
上述download视图中,通过给HttpResponse的close方法绑定清理函数,在响应完成(即文件下载完成)后自动执行以下操作:
- 删除本地文件
- 删除数据库中的
Fileupload记录 - 清除session中的文件ID,避免重复下载
内容的提问来源于stack exchange,提问作者NEERAJ S
相关产品推荐
相关产品推荐

