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


Posted in Python onDecember 01, 2020

最近在做一个仿知乎网站的项目了,里面涉及很多图片和文件上传。趁此机会我给大家总结下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自定义图片和文件上传路径(upload_to)的2种方式的文章就介绍到这了,更多相关Django 上传路径内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
从零学Python之引用和类属性的初步理解
May 15 Python
Python的Flask框架中配置多个子域名的方法讲解
Jun 07 Python
Python编程判断这天是这一年第几天的方法示例
Apr 18 Python
Django如何实现内容缓存示例详解
Sep 24 Python
python入门前的第一课 python怎样入门
Mar 06 Python
Python3处理HTTP请求的实例
May 10 Python
Python自定义一个类实现字典dict功能的方法
Jan 19 Python
python使用递归的方式建立二叉树
Jul 03 Python
python垃圾回收机制(GC)原理解析
Dec 30 Python
pip install命令安装扩展库整理
Mar 02 Python
解决pytorch 数据类型报错的问题
Mar 03 Python
python 对图片进行简单的处理
Jun 23 Python
使用python爬取抖音app视频的实例代码
Dec 01 #Python
基于Python实现粒子滤波效果
Dec 01 #Python
Django集成MongoDB实现过程解析
Dec 01 #Python
基于Django快速集成Echarts代码示例
Dec 01 #Python
Python更改pip镜像源的方法示例
Dec 01 #Python
Python读取图像并显示灰度图的实现
Dec 01 #Python
Python性能测试工具Locust安装及使用
Dec 01 #Python
You might like
php json_encode()函数返回json数据实例代码
2014/10/10 PHP
cakephp打印sql语句的方法
2015/02/13 PHP
JQuery与Ajax常用代码实现对比
2009/10/03 Javascript
javascript attachEvent绑定多个事件执行顺序问题
2010/10/20 Javascript
javascript读取Xml文件做一个二级联动菜单示例
2014/03/17 Javascript
IE 下Enter提交表单存在重复提交问题的解决方法
2014/05/04 Javascript
浅谈Javascript Base64 加密解密
2014/12/28 Javascript
javascript实现动态加载CSS
2015/01/26 Javascript
javascript实现点击后变换按钮显示文字的方法
2015/05/13 Javascript
jQuery EasyUI 菜单与按钮之创建简单的菜单和链接按钮
2015/11/18 Javascript
详解Bootstrap按钮
2016/01/04 Javascript
JS iFrame加载慢怎么解决
2016/05/13 Javascript
jQuery图片轮播插件——前端开发必看
2016/05/31 Javascript
基于jQuery实现火焰灯效果导航菜单
2017/01/04 Javascript
Angularjs修改密码的实例代码
2017/05/26 Javascript
JavaWeb表单及时验证功能在输入后立即验证(含用户类型,性别,爱好...的验证)
2017/06/09 Javascript
激动人心的 Angular HttpClient的源码解析
2017/07/10 Javascript
详解vue.js之props传递参数
2017/12/12 Javascript
python以环状形式组合排列图片并输出的方法
2015/03/17 Python
Python中逗号的三种作用实例分析
2015/06/08 Python
python简单判断序列是否为空的方法
2015/06/30 Python
Python中的函数式编程:不可变的数据结构
2018/10/08 Python
python去除拼音声调字母,替换为字母的方法
2018/11/28 Python
python 使用正则表达式按照多个空格分割字符的实例
2018/12/20 Python
使用Django简单编写一个XSS平台的方法步骤
2019/03/25 Python
pandas dataframe的合并实现(append, merge, concat)
2019/06/24 Python
python如何调用字典的key
2020/05/25 Python
使用anaconda安装pytorch的实现步骤
2020/09/03 Python
前端canvas动画如何转成mp4视频的方法
2019/06/17 HTML / CSS
命名空间(namespace)和程序集(Assembly)有什么区别
2015/09/25 面试题
实习生岗位职责
2014/04/12 职场文书
低碳生活倡议书
2014/04/14 职场文书
全国文明单位申报材料
2014/05/31 职场文书
2016中考冲刺决心书
2015/09/22 职场文书
html5实现点击弹出图片功能
2021/07/16 HTML / CSS
解析MySQL索引的作用
2022/03/03 MySQL