详解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使用random和tertools模块解一些经典概率问题
Jan 28 Python
使用Node.js和Socket.IO扩展Django的实时处理功能
Apr 20 Python
Python实现的爬取网易动态评论操作示例
Jun 06 Python
利用Python实现微信找房机器人实例教程
Mar 10 Python
Python可变和不可变、类的私有属性实例分析
May 31 Python
python的set处理二维数组转一维数组的方法示例
May 31 Python
利用python实现PSO算法优化二元函数
Nov 13 Python
使用Python+selenium实现第一个自动化测试脚本
Mar 17 Python
Python实现密钥密码(加解密)实例详解
Apr 26 Python
使用tensorflow进行音乐类型的分类
Aug 14 Python
python计算auc的方法
Sep 09 Python
python通配符之glob模块的使用详解
Apr 24 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边学边教》(02.Apache+PHP环境配置――下篇)
2006/12/13 PHP
php实现从ftp服务器上下载文件树到本地电脑的程序
2009/02/10 PHP
php中使用explode查找某个字符是否存在的方法
2011/07/12 PHP
php中memcache 基本操作实例
2015/05/17 PHP
twig模板获取全局变量的方法
2016/02/05 PHP
Yii2针对游客、用户防范规则和限制的解决方法分析
2016/10/08 PHP
Code: write(s,d) 输出连续字符串
2007/08/19 Javascript
学习面向对象之面向对象的术语
2010/11/30 Javascript
jquery lazyload延迟加载技术的实现原理分析
2011/01/24 Javascript
JQuery EasyUI 加载两次url的原因分析及解决方案
2014/08/18 Javascript
JQuery用户名校验的具体实现
2016/03/18 Javascript
React.js入门实例教程之创建hello world 的5种方式
2016/05/11 Javascript
jQuery dataTables与jQuery UI 对话框dialog的使用教程
2016/09/02 Javascript
JavaScript实现的搜索及高亮显示功能示例
2017/08/14 Javascript
AngularJS+Bootstrap3多级导航菜单的实现代码
2017/08/16 Javascript
安装vue-cli报错 -4058 的解决方法
2017/10/19 Javascript
微信小程序:数据存储、传值、取值详解
2019/05/07 Javascript
通过实例了解Javascript柯里化流程
2020/03/03 Javascript
Ant-design-vue Table组件customRow属性的使用说明
2020/10/28 Javascript
[51:26]DOTA2上海特级锦标赛主赛事日 - 2 胜者组第一轮#3Secret VS OG第二局
2016/03/03 DOTA
[35:34]Liquid vs Winstrike 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
解决Python传递中文参数的问题
2015/08/04 Python
Python使用zip合并相邻列表项的方法示例
2018/03/17 Python
Django-Rest-Framework 权限管理源码浅析(小结)
2018/11/12 Python
Python使用MyQR制作专属动态彩色二维码功能
2019/06/04 Python
Pycharm2020.1安装无法启动问题即设置中文插件的方法
2020/08/07 Python
10 套华丽的CSS3 按钮小结
2012/10/03 HTML / CSS
SEPHORA丝芙兰德国官方购物网站:化妆品、护肤品和香水
2020/01/21 全球购物
爱情寄语大全
2014/04/09 职场文书
党员“四风”问题批评与自我批评思想汇报
2014/10/06 职场文书
2015年元旦主持词结束语
2014/12/14 职场文书
市场总监岗位职责
2015/02/11 职场文书
2015年学校办公室主任工作总结
2015/07/20 职场文书
创业分两种人:那么哪些适合创业?,哪些适合不适合创业呢?
2019/08/23 职场文书
Python使用OpenCV和K-Means聚类对毕业照进行图像分割
2021/06/11 Python
redis数据一致性的实现示例
2022/03/18 Redis