首页 > 解决方案 > 如何生成 Django 一次性下载链接

问题描述

我想在我的项目中保护可下载文件,但不知道如何实现。每次调用 post_detail 视图时,都会生成一个新的下载链接,有效期为 60 分钟,并且也只能是访问链接。

模型.py

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(verbose_name="Post Title", max_length=25)
    content = models.TextField(verbose_name="Post Content", max_length=5000)
    tag = models.CharField(verbose_name="Tags/Meta - (sep. by comma)", max_length=50, blank=True)
    category = models.ForeignKey(Category, verbose_name="Category", on_delete=models.CASCADE, null=True)
    postattachment = fields.FileField(
        verbose_name="Post Attachment",
        blank=True,
        null=True,
        upload_to=get_file_path_user_uploads,
        validators=[file_extension_postattachment, file_size_postattachment]

    published_date = models.DateField(auto_now_add=True, null=True)


    def publish(self):
        self.published_date = timezone.now()
        self.save()

    class Meta:
        verbose_name = "Post"
        verbose_name_plural = "Post(s)"
        ordering = ['-title']

    def __str__(self):
        return self.title

视图.py

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    list_comments = Comment.objects.get_queryset().filter(post_id=pk).order_by('-pk')
    paginator = Paginator(list_comments, 10)
    page = request.GET.get('commentpage')
    comments = paginator.get_page(page)
    return render(request, 'MyProject/post_detail.html', {'post': post, 'comments': comments})

如果 也许有一些实践示例会很有帮助。

提前致谢

标签: pythondjangosecurityhyperlinkdownload

解决方案


我不是 Django 专家,但我认为这是纯粹在 Django 中无法实现的。一旦你的请求在 Django 中被执行,也就是说,你已经成功地为你的用户生成了一个下载链接,你不能在 60 分钟后返回并使其无效。不是纯粹的 Django(修复我!)。

另一个阻塞的原因是 Django 根本不是为提供文件而设计的。文件(静态和媒体)旨在由您在 Django 前面的网络服务器(apache/nginx/etc...)提供服务。例如,可以通过如下链接访问 Django 存储的文件:https://my-django-app.venom.com/media/my_file.jpg

这里的问题是,您的文件的位置很容易被猜到。为了更难猜,你应该把它放在一个带有长随机字符串的文件夹中,像这样: https://my-django-app.venom.com/media/b926yqagf6qrzpyew7h3kghtejayxp/my_file.jpg.

为了实现这样的功能,我看到了两种方法(可能有几十种其他选择,但我立刻想到了这两种):

生成和删除随机路径

要在 60 分钟后使路径无效,您必须按顺序对每个文件请求执行以下操作:

  1. 生成随机字符串
  2. 使用生成的随机字符串作为名称创建文件夹
  3. 将要提供的文件复制到该文件夹​​中(文件的原始版本应存储在您的MEDIA文件夹之外以提高安全性
  4. 向用户展示生成链接
  5. 在某处注册此 URL 并为其设置过期日期,例如,从生成后 60 分钟(我会为此创建一个简单的模型并将其存储在 SQL 中)
  6. 每分钟在存储的 URL 上运行一个作业,如果它过期,从文件系统中删除它的文件夹

要实现第 6 步,您必须使用 Celery 扩展您的 Django 应用程序。使用 Celery,您可以轻松地安排工作(google for celery-beat)。该作业将每分钟执行一次(或任何您喜欢的),查询过去当前时间存储的 URL,并从文件系统上的文件夹中删除随机字符串文件夹及其内容MEDIA。Celery 超级简单,网上有几十个很好的例子。

使用对象存储服务器管理 Django 外部的过期链接

通过将用户上传的内容存储在对象存储(例如 minio)中,可以很容易地在基础架构级别管理过期链接。Minio 与 Amazon S3 非常相似,但它是开源的,可以托管在您自己的场所。Minio 可以为存储的文件生成链接,您可以将过期时间设置在 1 分钟到 1 周之间。在 Django 中,您所要做的就是从 minio 请求链接并指定过期时间。其余的由 minio 管理。

要实现这种方法,您必须扩展 Django 的文件存储 API ( https://docs.djangoproject.com/en/2.1/ref/files/storage/ ) 并利用为 Django 编写的 minio 客户端之一。我推荐 django-minio-storage ( https://github.com/py-pa/django-minio-storage )。

如果您采用这种方法,您可以将 Django 与用户上传的内容完全分离,并停止依赖您的网络服务器从 Django 的MEDIA文件夹中提供文件。

祝你好运!

2021 年更新:

自从我上次更新以来,我创建了自己的 Django StorageBackend 实现以轻松与 MinIO 交互。我尽我所能保持这个库是最新的,但总是欢迎请求请求:https ://github.com/theriverman/django-minio-backend


推荐阅读