如何获取Django中request.FILES['file']的完整路径作为下载链接?
Django获取上传文件完整路径以生成有效下载链接
我已经实现了将文件上传至Django模型的功能,上传的文件是InMemoryUploadedFile对象。现在需要获取request.FILES['file']对应的文件完整路径,用来设置有效的下载链接。之前尝试用request.open(file),但满足不了需求。相关代码如下:
models.py
import hashlib from django.db import models class Document(models.Model): """上传文件类,存储文件和描述 配合表单使用 """ id = models.AutoField(primary_key=True) file_path = models.TextField(max_length=255) hash_size = models.CharField(max_length=255) def __str__(self): return f"{self.id},{self.file_path},{self.hash_size}" def get_hash(file: object) -> str: md = hashlib.md5() # with open(file, "rb") as f: for chunk in iter(lambda: file.read(4096), b""): md.update(chunk) return md.hexdigest()
views.py
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest, redirect from django.shortcuts import render from .models import Document from .forms import DocumentForm def get_file_size(file): # 假设这是判断文件大小的函数,原代码未给出实现 return file.size <= 300 * 1024 * 1024 def model_form_upload_1(request: HttpRequest) -> HttpResponse: '''处理表单的视图,文件数据会在request.FILES中接收''' if request.method == 'POST': upload = request.FILES['file'] path = upload.chunks() print(path) hash_tuple = () form = DocumentForm(request.POST, request.FILES) if form.is_valid(): print(upload.size) if get_file_size(upload): hash = Document.get_hash(upload) hash_list = [x for x in Document.objects.all().values_list('hash_size')] hash_tuple += (hash,) if hash_tuple not in hash_list: Document.objects.create( file_path=request.GET.get(upload), hash_size=hash) else: return HttpResponseBadRequest("文件已存在") else: return HttpResponseBadRequest("文件超过300MB") return redirect("view") else: return HttpResponseBadRequest("表单无效") else: form = DocumentForm() return render( request=request, template_name='model_form_upload.html', context={'form': form}) return render(request, 'model_form_upload.html')
下载相关视图函数
from django.core.files.storage import FileSystemStorage from django.http import HttpResponse def simple_view(request): in_memory_file_obj = request.FILES["file"] FileSystemStorage(location="/tmp").save(in_memory_file_obj.name, in_memory_file_obj) return HttpResponse("Success")
模板代码(model_form_upload.html片段)
<tr> <td>{{document.id}}</td> <td>{{document.file_path}}</td> <td><a href="{{ document.file_path }}">download</a></td> <td>{{document.hash_size}}</td> </tr>
问题分析与解决方法
核心问题
InMemoryUploadedFile是内存中的临时文件对象,本身没有真实的磁盘路径。必须先将文件保存到服务器磁盘的指定目录,再把保存后的路径存入数据库,才能生成可访问的下载链接。
具体实现步骤
配置Django文件存储路径
在项目settings.py中添加媒体文件的存储配置:MEDIA_ROOT = BASE_DIR / 'media' # 文件会存在项目根目录的media文件夹下 MEDIA_URL = '/media/' # 访问文件的URL前缀同时在项目根目录的
urls.py中添加媒体文件的路由映射:from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # 其他路由配置 ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)修改模型与视图,正确保存文件路径
- 推荐用
FileField替代TextField存储文件,Django会自动处理文件存储和路径管理:class Document(models.Model): id = models.AutoField(primary_key=True) file = models.FileField(upload_to='documents/') # 文件自动存在media/documents/目录下 hash_size = models.CharField(max_length=255) def __str__(self): return f"{self.id},{self.file.name},{self.hash_size}" - 如果坚持用
TextField存路径,修改视图中的文件保存逻辑:from django.core.files.storage import default_storage def model_form_upload_1(request: HttpRequest) -> HttpResponse: if request.method == 'POST': upload = request.FILES['file'] form = DocumentForm(request.POST, request.FILES) if form.is_valid(): if get_file_size(upload): # 计算哈希前重置文件指针(避免读取位置错误) upload.seek(0) hash = Document.get_hash(upload) # 检查文件是否已存在 if not Document.objects.filter(hash_size=hash).exists(): # 将文件保存到MEDIA_ROOT下的documents目录 file_path = default_storage.save(f'documents/{upload.name}', upload) # 保存相对路径到数据库 Document.objects.create( file_path=file_path, hash_size=hash) else: return HttpResponseBadRequest("文件已存在") else: return HttpResponseBadRequest("文件超过300MB") return redirect("view") else: return HttpResponseBadRequest("表单无效") else: form = DocumentForm() return render(request, 'model_form_upload.html', {'form': form})
- 推荐用
生成有效下载链接
在模板中拼接MEDIA_URL和数据库中存储的文件路径:{% load static %} <tr> <td>{{document.id}}</td> <td>{{document.file_path}}</td> <td><a href="{% get_media_prefix %}{{ document.file_path }}">download</a></td> <td>{{document.hash_size}}</td> </tr>修复现有代码的错误点
Document.get_hash需要定义为静态方法,添加@staticmethod装饰器,否则调用会出错;- 原代码中
request.GET.get(upload)是错误逻辑,GET参数是字符串,无法直接传入文件对象,必须替换为实际保存后的文件路径; - 文件大小判断的分支逻辑需要对应,确保超过300MB时返回正确提示。
内容的提问来源于stack exchange,提问作者Mytiaiev
相关产品推荐
相关产品推荐

