详解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 相关文章推荐
在Django的视图中使用数据库查询的方法
Jul 16 Python
python基础教程项目五之虚拟茶话会
Apr 02 Python
pycharm 解除默认unittest模式的方法
Nov 30 Python
pandas进行时间数据的转换和计算时间差并提取年月日
Jul 06 Python
Python Numpy库datetime类型的处理详解
Jul 13 Python
python用线性回归预测股票价格的实现代码
Sep 04 Python
基于python实现语音录入识别代码实例
Jan 17 Python
python生成13位或16位时间戳以及反向解析时间戳的实例
Mar 03 Python
Nginx+Uwsgi+Django 项目部署到服务器的思路详解
May 08 Python
快速解释如何使用pandas的inplace参数的使用
Jul 23 Python
python爬虫爬取网页数据并解析数据
Sep 18 Python
python3实现无权最短路径的方法
May 12 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 calender(日历)二个版本代码示例(解决2038问题)
2013/12/24 PHP
PHP文件上传类实例详解
2016/04/08 PHP
thinkphp5框架路由原理与用法详解
2020/02/11 PHP
JS宝典学习笔记(下)
2007/01/10 Javascript
for 循环性能比较 提高for循环的效率
2009/03/19 Javascript
javascript 日历提醒系统( 兼容所有浏览器 )
2009/04/07 Javascript
google地图的路线实现代码
2009/08/20 Javascript
一个js的tab切换效果代码[代码分离]
2010/04/11 Javascript
javascript实现的使用方向键控制光标在table单元格中切换
2010/11/17 Javascript
jQuery获取样式中的背景颜色属性值/颜色值
2012/12/17 Javascript
遍历DOM对象内的元素属性示例代码
2014/02/08 Javascript
js charAt的使用示例
2014/02/18 Javascript
javascript实现简单的省市区三级联动
2015/05/14 Javascript
用window.onerror捕获并上报Js错误的方法
2016/01/27 Javascript
基于JS代码实现实时显示系统时间
2016/06/16 Javascript
用原生js做单页应用
2017/01/17 Javascript
underscore之Collections_动力节点Java学院整理
2017/07/10 Javascript
webpack处理 css\less\sass 样式的方法
2017/08/21 Javascript
vue如何通过id从列表页跳转到对应的详情页
2018/05/01 Javascript
微信小程序五子棋游戏的棋盘,重置,对弈实现方法【附demo源码下载】
2019/02/20 Javascript
JS中的算法与数据结构之队列(Queue)实例详解
2019/08/20 Javascript
vue多个元素的样式选择器问题
2019/11/29 Javascript
详解vue3.0 diff算法的使用(超详细)
2020/07/01 Javascript
Python内置函数reversed()用法分析
2018/03/20 Python
python-tkinter之按钮的使用,开关方法
2019/06/11 Python
Python将二维列表list的数据输出(TXT,Excel)
2020/04/23 Python
利用PyTorch实现VGG16教程
2020/06/24 Python
解决运行出现'dict' object has no attribute 'has_key'问题
2020/07/15 Python
html5触摸事件判断滑动方向的实现
2018/06/05 HTML / CSS
力学专业毕业生自荐信
2013/11/17 职场文书
镇政府副镇长群众路线专题民主生活会对照检查材料
2014/09/19 职场文书
2016年国庆节宣传标语
2015/11/25 职场文书
用基于python的appium爬取b站直播消费记录
2021/04/17 Python
Python游戏开发实例之graphics实现AI五子棋
2021/11/01 Python
关于使用Redisson订阅数问题
2022/01/18 Redis
配置Kubernetes外网访问集群
2022/03/31 Servers