Django自定义图片和文件上传路径(upload_to)的2种方式

最近在做一个仿知乎网站的项目了,里面涉及很多图片和文件上传。趁此机会我给大家总结下Django自定义图片和文件上传路径的2种方式吧。

 

方法1: 在Django模型中定义upload_to选项。

Django模型中的ImageField和FileField的upload_to选项是必填项,其存储路径是相对于MEIDA_ROOT而来的。

 

我们来看一个简单案例(如下所示)。如果你的MEDIA_ROOT是/media/文件夹,而你的上传文件夹upload_to=“avatar", 那么你上传的文件会自动存储到/media/avatar/文件夹。

 

class UserProfile(models.Model):

    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    avatar = models.ImageField(upload_to='avatar', verbose_name="头像")
  

如果你的文件名是sky.jpg, 那么图片上传后数据库中的avatar字段为avatar/sky.jpg, 该字段指向图片对象,而非绝对路径。要在模板中使用该图片,应该使用avatar.url (即/media/avatar/sky.jpg)。

 

但在实际应用中,请千万别这么做。这里有2个严重问题。

  • 所有用户都把头像上传到了同一个avatar文件夹了

  • 原文件名是什么,那么新文件名就是什么

试想用户很多,很可能发生文件重名问题,造成后来用户上传的文件把前面用户上传的头像覆盖了,造成了用户A挂用户B头像的状况。

 

正确的做法是动态定义上传路径,把图片存储到用户自己的文件夹下,并对其重命名。如下图所示。这样图片就会保存在/media/1/avatar/里了,而且文件以uuid命名。

 

from django.db import models
from django.contrib.auth.models import User
import uuid

# Create your models here.

def user_directory_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = '{}.{}'.format(uuid.uuid4().hex[:8], ext)
    # return the whole path to the file
    return "{0}/{1}/{2}".format(instance.user.id, "avatar", filename)

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    avatar = models.ImageField(upload_to=user_directory_path, verbose_name="头像")

上述案例显然还有一个问题,不同系统路径分隔符/和\是不一样的,为保证代码在不同系统中能重用,更好的方式是使用python的os模块来拼接路径。如下图所示。

 

from django.db import models
from django.contrib.auth.models import User
import uuid
import os

# Create your models here.

def user_directory_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
    # return the whole path to the file
    return os.path.join(instance.user.id, "avatar", filename)

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    avatar = models.ImageField(upload_to=user_directory_path, verbose_name="头像")
  

用户上传文件可能是图片,也可能是pdf文件,我们如何把它们放在同一用户的不同文件夹下呢?实现这个很简单,如下图所示。

 

def user_directory_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = '{}.{}'.format(uuid.uuid4().hex[:8], ext)
    sub_folder = 'file'
    if ext.lower() in ["jpg", "png", "gif"]:
        sub_folder = "avatar"
    if ext.lower() in ["pdf", "docx"]:
        sub_folder = "document"
    return os.path.join(instance.user.id, sub_folder, filename)

 

方法2: 在视图中自定义上传图片或文件路径

方法1最简单直白,但有一个较大缺陷,文件上传后未经处理就直接存储了。假如用户上传了图片,我们希望先对其压缩或裁剪,然后再存储,或者我们不希望上传图片或文件到默认的路径,这时我们就有必要在视图中自定义图片或文件路径了。例子如下。

 

@login_required
def ajax_avatar_upload(request):
    user = request.user
    user_profile = get_object_or_404(UserProfile, user=user)

    if request.method == "POST":
        form = AvatarUploadForm(request.POST, request.FILES)
        if form.is_valid():
            img = request.FILES['avatar_file']  # 获取上传图片
            cropped_avatar = crop_image(img, user.id)
            user_profile.avatar = cropped_avatar  # 将图片路径修改到当前会员数据库
         user_profile.save()
    return HttpResponseRedirect(reverse('myaccount:profile'))


def crop_image(file, uid):

    # 随机生成新的图片名,自定义路径。
   ext = file.name.split('.')[-1]
    file_name = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
    cropped_avatar = os.path.join(uid, "avatar", file_name)
    # 相对根目录路径
   file_path = os.path.join("media", uid, "avatar", file_name)

    # 裁剪图片,压缩尺寸为200*200。
   img = Image.open(file)
    crop_im = img.crop((50,50,300, 300)).resize((200, 200), Image.ANTIALIAS)
    crop_im.save(file_path)

    return cropped_avatar


Django Web开发核心基础知识

Django网站开发四件套是如何遵循MVC软件设计模式的?

Django基础核心技术介绍(1): Model模型的介绍与设计

Django基础核心技术介绍(2): URL的设计与配置

Django基础核心技术介绍(3): View视图详解与通用视图

Django基础核心技术介绍(4): Template模板的编写及过滤器

Django基础核心结束介绍(5): Forms表单的使用与设计

Django基础(6): 模型Models高级进阶必读。

Django基础(7): cookie和session应用场景及如何使用

Django基础(8): 缓存Cache应用场景及工作原理,Cache设置及如何使用

Django基础(9): 表单Forms的高级使用技巧

Django基础(10): URL重定向的HttpResponseDirect, redirect和reverse的用法

Django基础(11): 表单集合Formset的高级用法

Django基础(12): Request对象详解及开发显示用户IP地址和浏览器APP

Django基础(13): 深夜放干货。QuerySet特性及高级使用技巧,如何减少数据库的访问,节省内存,提升网站性能。

Django基础(14): 通过next参数实现登录后跳转回到前一页的3种方法

Django基础(15): 模板过滤器(filter)的工作原理及如何自定义模板过滤器

Django基础(16): 模板标签(tags)的分类及如何自定义模板标签

Django基础(17): 如何上传处理文件及Ajax文件上传示范(附GitHub源码)

Django基础(18): 实现文件下载的3种方法及文件私有化

Django基础(19): Django Admin管理后台详解(上)

Django基础(20): Django admin管理后台详解(中)如何自定义list_display和list_filter

Django基础(21): Django admin管理后台详解(下)如何自定义actions, 表单和美化admin

Django基础(22): 数据库的设计之自定义表名,建立索引和使用多数据库主从配置

Django基础(23): 权限管理(permissions)与用户组(group)详解

Django基础(24): aggregate和annotate方法使用详解与示例

Django基础(25):settings.py设置选项深入解读。大江狗精品原创。

Django基础(26): 常用装饰器应用场景及正确使用方法

Django基础(27): 快捷函数(shortcut function)模块详解Django基础(28): 如何设计充满陷阱的优美URL

Django基础(28): 如何设计充满陷阱的优美URL

 

Django Web开发实战案例

Django 2.0 项目实战(1): 扩展Django自带User模型,实现用户注册与登录

Django 2.0 项目实战(2): 编辑用户个人资料,扩展Django后台UserAdmin

Django 2.0项目实战(3): 密码重置与退出登录

Django 2.0 项目实战: 图片上传与显示

Django 2.0 项目实战: PDF文件页面提取

Django 2.0 项目实战: PDF文件合并

Django 2.0 项目实战:输出树形分类目录

Django 2.0 项目实战: 网页计数器统计浏览次数

Django 2.0 项目实战:  利用AJAX实现博文实时搜索

Django 1.X和2.0下利用自带分页Paginator类实现分页功能

Django实战: 利用Ajax生成联动下拉菜单

世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(1)

世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(2)

django-allauth教程(1): 安装,用户注册,登录,邮箱验证和密码重置(更新)

django-allauth教程(2): 用户个人资料UserProfile扩展与编辑

django-allauth教程(3): 第三方账户授权登录(以百度账号为例)

django-allauth教程(4): 美化模板,自定义邮件和消息内容

Django+jQuery cropper实现用户头像裁剪, 预览和上传[原创]

Django实战教程: 开发餐厅在线点评网站(1)

Django实战教程: 开发餐厅在线点评网站(2)

Django实战教程: 开发企业级应用智能文档管理系统smartdoc(1)

Django实战教程: 开发企业级应用智能文档管理系统smartdoc(2)之权限管理

Django实战教程: 开发企业级应用智能文档管理系统smartdoc(3)附GitHub代码地址

Django实战专题: 开发专业博客(1)之内容管理后台开发

Django实战专题: 开发专业博客(2)之母子类别导航和添加富文本编辑器CKEditor

Django实战专题: 开发专业博客(3)之仿微信评论点赞功能

Django实战: Python爬取链家上海二手房信息,存入数据库并在前端显示

Django应用实战: 编写你自己的PDF编辑器, 实现PDF页面提取, 页面合并与替换。

如何在阿里云Ubuntu服务器通过uWSGI和Nginx部署Django项目教程-大江狗原创出品

 

Django Web开发学习笔记

浅谈Django Model创建对象的save与create方法

Django模板设置全局变量(默认变量)

Django常用命令django-admin.py和manage.py用法详解

Django自定义图片和文件上传路径(upload_to)的2种方式

Django ContentTypes框架详解及使用场景介绍

Django更改模型过程中易出现的问题及解决方案

2019新年第一篇: SQLite的优缺点及Django配置MySQL数据库

你可能感兴趣的:(Django,Django基础连载)