python - 如何生成 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})
如果 也许有一些实践示例会很有帮助。
提前致谢
解决方案
我不是 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 分钟后使路径无效,您必须按顺序对每个文件请求执行以下操作:
- 生成随机字符串
- 使用生成的随机字符串作为名称创建文件夹
- 将要提供的文件复制到该文件夹中(文件的原始版本应存储在您的
MEDIA
文件夹之外以提高安全性 - 向用户展示生成链接
- 在某处注册此 URL 并为其设置过期日期,例如,从生成后 60 分钟(我会为此创建一个简单的模型并将其存储在 SQL 中)
- 每分钟在存储的 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
推荐阅读
- javascript - 为什么要水。身体在快递中未定义
- android - Flutter 发布应用比调试版慢
- php - 用于多媒体处理的 RabbitMQ 消费者:最好的方法是什么?
- android - 片段未正确显示
- components - 在 Svelte 中创建可重用的模态组件的最佳实践?
- spring-boot - 使用 k8s hostNetwork 标志后,configMap 值不会注入到 springboot 应用程序中
- python - 我在熊猫中的位置列包含从 1 到 15 的值,并且投票百分比列包含从 0 到 100 的值
- reactjs - 当我在 ReactJS 应用程序中单击谷歌地图标记时,我想显示信息窗口?
- typescript - TypeScript 类型缩小在循环时不起作用
- html - 尽管两边都有自动边距,但内容仍然在子页面上水平移动