1. 上传文件相关字段类型

  • FileField:上传文件
  • FilePathField:服务器上的文件
  • ImageField:上传图片

1.1. 注意!

  • 一定要注意文件内容和格式!
  • 务必对所有上传文件进行安全检查!
  • 如果你不加任何检查就盲目的让任何人上传文件到你的服务器文档根目录内,比如上传了一个CGI或者PHP脚本,很可能就会被访问的用户执行,这具有致命的危害。

2. 基础设置步骤

2.1. 设置上传路径

  1. 设置 MEDIA_ROOT(settings.py)
    1. TEMPLATES 中 context_processors 添加: ‘django.template.context_processors.media’,
    2. MEDIA_ROOT 基本路径,注意读写权限 os.path.join(BASE_DIR, ‘folder/‘)
    3. MEDIA_URL 访问文件的路径,比如:MEDIA_URL = ‘/upload/‘
  2. 设置上传文件字段路径(models.py)
  3. 配置根 URLconf
  1. from .settings import MEDIA_ROOT
  2. from django.views.static import serve
  3. from django.conf.urls import url
  4. urlpatterns=[
  5. #...
  6. url(r'^upload/(?P<path>.*)$', serve, {'document_root':MEDIA_ROOT}),
  7. ]

3. 示例

3.1. FileField 上传文件

3.1.1. 设置 upload_to 路径

  1. class MyModel(models.Model):
  2. # 上传位置:MEDIA_ROOT/uploads/
  3. upload = models.FileField(upload_to='uploads/')
  4. # 上传位置:MEDIA_ROOT/uploads/2019/09/09/
  5. upload = models.FileField(upload_to='uploads/%Y/%m/%d/')

3.1.2. 回调函数上传

  1. def user_directory_path(instance, filename):
  2. """上传到用户路径
  3. Args:
  4. instance: 当前数据记录
  5. filename: 文件名
  6. Returns:
  7. str: Unix风格字符串
  8. """
  9. # 上传位置:MEDIA_ROOT/user_<id>/<filename>
  10. return 'user_{}/{}'.format(instance.user.id, filename)
  11. class MyModel(models.Model):
  12. upload = models.FileField(upload_to=user_directory_path)

3.2. ImageField 上传图片

使用前需安装 Pillow:$ pip3 install pillow

与 FileField 相似,只是多了:

  1. height_field 参数:记录图片的高度
  2. width_field 参数:同上,记录宽度

3.3. 模板中使用

比如 ImageField 字段名叫:mug_shot

  • {{ objName.mug_shot.url }} 文件URL
  • {{ objName.mug_shot.name }} 名称
  • {{ objName.mug_shot.size }} 大小

4. 上传文件提供有限的访问

  1. 根 URLconf 中设置的 url(r'^media/(?P<path>.*)$', serve, {'document_root':MEDIA_ROOT}), 只是在 python manage.py runserver 本地服务器中使用的
  2. 部署到IIS中就可以注释上面这一项
  3. 提供有限访问的方法

4.1. 提供有限访问的方法

比如,只想让拥有浏览下载权限的用户下载文件,其余的不可以下载

可以参考 使用StreamHttpResponse和FileResponse下载文件的注意事项及文件私有化

  1. # 应用 urls.py
  2. #...
  3. urlpatterns = [
  4. #...
  5. path('serve_files/<int:file_id>/', fore.serve_files, name='serve_files'), # 提供媒体文件
  6. ]
  7. # views.py
  8. from django.http import FileResponse
  9. @permission_required('inna.view_download')
  10. def serve_files(request, file_id):
  11. """提供文件
  12. 只有查看下载权限的用户可以得到文件
  13. """
  14. # 获取文件对象
  15. downloadable_file = get_object_or_404(Download, id=file_id)
  16. file_upload_list = downloadable_file.file_upload.url.split('/')
  17. file_name_list = file_upload_list[3].split('.')
  18. filename = file_name_list[0]
  19. ext = file_name_list[1]
  20. # 增加下载数
  21. downloadable_file.reader += 1
  22. downloadable_file.save()
  23. # 匹配 MIME 类型
  24. ext_content_type = {
  25. 'xls': 'application/vnd.ms-excel',
  26. 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  27. 'doc': 'application/msword',
  28. 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  29. 'pdf': 'application/pdf',
  30. 'stp': 'application/stp',
  31. 'step': 'application/stp',
  32. 'cad': 'application/cad',
  33. }
  34. file_path = os.path.join(settings.MEDIA_ROOT, 'download', filename +'.'+ ext)
  35. the_file = open(file_path, 'rb')
  36. file_response = FileResponse(the_file)
  37. file_response['content_type'] = ext_content_type[ext]
  38. file_response['Content-Disposition'] = 'attachment; filename=' + filename +'.'+ ext
  39. return file_response
  1. <!-- templates.html -->
  2. <a target="_blank" href="{% url 'inna:serve_files' file.id %}">