使用Django或任何Python模块生成链接

3 投票
3 回答
13494 浏览
提问于 2025-04-16 13:47

我想为我的用户生成一个临时下载链接。请问用Django生成链接,使用URL模式这样做可以吗?这样做是否正确?因为我可能不太理解某些流程是如何运作的,这可能会导致我的内存溢出或者其他问题。如果能给我一些例子或者工具的推荐就太好了,可能还需要一些nginx或apache的模块?

我想要实现的是,创建一个根据用户和时间生成的URL模式。然后解密这个链接,并在视图中返回一个文件。

3 个回答

1

听起来你是在建议使用某种动态的URL配置。

为什么不简单点,设置一个单一的URL,来捕捉一个很长的编码字符串,这个字符串可以根据用户或时间来变化呢?

(r'^download/(?P<encrypted_id>(.*)/$', 'download_file'), # use your own regexp


def download_file(request, encrypted_id):
    decrypted = decrypt(encrypted_id)
    _file = get_file(decrypted)
    return _file

很多网站也会使用GET参数。

比如:www.example.com/download_file/?09248903483o8a908423028a0df8032

如果你担心性能问题,可以看看这个帖子里的回答:让Django提供可下载文件

帖子中提到了使用apache的x-sendfile模块。


另一个选择是直接重定向到Django以某种方式提供的静态文件。

4

一个简单的方案是使用用户名和时间戳的哈希值:

from datetime import datetime
from hashlib import sha1

user = 'bob'
time = datetime.now().isoformat()
plain = user + '\0' + time
token = sha1(plain)
print token.hexdigest()
"1e2c5078bd0de12a79d1a49255a9bff9737aa4a4"

接下来,你把这个令牌存储在一个内存缓存中,并设置一个过期时间。这样,任何一台你的网页服务器都可以访问这个令牌,而且它会自动过期。最后,给Django添加一个处理网址的功能,网址格式是'^download/.+',这个功能会在内存缓存中查找这个令牌,以确定它是否有效。你甚至可以把要下载的文件名作为令牌的值存储在内存缓存中。

2

是的,让Django生成网址是可以的。这和用urls.py处理网址是两回事。通常,你不希望Django来处理文件的提供,关于这一点可以参考静态文件的文档[1],所以不要再想着用网址模式了。

你可以考虑用哈希算法生成一个随机的密钥,比如md5或sha1。然后把文件、密钥和添加的时间存到数据库里,创建一个可以被你的网络服务器(比如apache或nginx,建议用nginx)访问的下载目录。因为这些文件是临时的,你需要设置一个定时任务,检查生成网址的时间是否过期,过期后就清理文件并删除数据库中的记录。这应该是一个Django命令,可以通过manage.py来执行。

请注意,这段代码只是为了这个例子写的,没经过测试!它可能无法按照你想要的方式工作,但确实是可行的。如果你想让下载链接有密码保护,可以看看httpbasic auth。你可以在创建链接或注册时,使用htpasswd和subprocess模块动态生成和删除httpd.auth文件中的记录。

import hashlib, random, datetime, os, shutil
# model to hold link info. has these fields: key (charfield), filepath (filepathfield)
# datetime (datetimefield), url (charfield), orgpath (filepathfield of the orignal path
# or a foreignkey to the files model.
from models import MyDlLink 
# settings.py for the app
from myapp import settings as myapp_settings

# full path and name of file to dl.
def genUrl(filepath):
  # create a onetime salt for randomness
  salt = ''.join(['{0}'.format(random.randrange(10) for i in range(10)])
  key = hashlib('{0}{1}'.format(salt, filepath).hexdigest()
  newpath = os.path.join(myapp_settings.DL_ROOT, key)
  shutil.copy2(fname, newpath)
  newlink = MyDlink()
  newlink.key = key
  newlink.date = datetime.datetime.now()
  newlink.orgpath = filepath
  newlink.newpath = newpath
  newlink.url = "{0}/{1}/{2}".format(myapp_settings.DL_URL, key, os.path.basename(fname))

  newlink.save()
  return newlink


# in commands
def check_url_expired():
  maxage = datetime.timedelta(days=7)
  now = datetime.datetime.now()
  for link in MyDlink.objects.all():
     if(now - link.date) > maxage:
       os.path.remove(link.newpath)
           link.delete()

[1] http://docs.djangoproject.com/en/1.2/howto/static-files/

撰写回答