You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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是内存中的临时文件对象,本身没有真实的磁盘路径。必须先将文件保存到服务器磁盘的指定目录,再把保存后的路径存入数据库,才能生成可访问的下载链接。

具体实现步骤

  1. 配置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)
    
  2. 修改模型与视图,正确保存文件路径

    • 推荐用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})
      
  3. 生成有效下载链接
    在模板中拼接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>
    
  4. 修复现有代码的错误点

    • Document.get_hash需要定义为静态方法,添加@staticmethod装饰器,否则调用会出错;
    • 原代码中request.GET.get(upload)是错误逻辑,GET参数是字符串,无法直接传入文件对象,必须替换为实际保存后的文件路径;
    • 文件大小判断的分支逻辑需要对应,确保超过300MB时返回正确提示。

内容的提问来源于stack exchange,提问作者Mytiaiev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 17:25:25