详解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 字符串操作方法大全
Mar 11 Python
使用C语言扩展Python程序的简单入门指引
Apr 14 Python
Swift中的协议(protocol)学习教程
Jul 08 Python
Python 查找字符在字符串中的位置实例
May 02 Python
python检测IP地址变化并触发事件
Dec 26 Python
python 监听salt job状态,并任务数据推送到redis中的方法
Jan 14 Python
Django项目使用CircleCI的方法示例
Jul 14 Python
python 根据网易云歌曲的ID 直接下载歌曲的实例
Aug 24 Python
pyspark给dataframe增加新的一列的实现示例
Apr 24 Python
python如何调用字典的key
May 25 Python
Python如何脚本过滤文件中的注释
May 27 Python
Python实现Telnet自动连接检测密码的示例
Apr 16 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中HTTP方式下的Gzip压缩传输方法举偶
2007/02/15 PHP
php 数学运算验证码实现代码
2009/10/11 PHP
解析php类的注册与自动加载
2013/07/05 PHP
php使用Jpgraph绘制3D饼状图的方法
2015/06/10 PHP
PHP中如何使用session实现保存用户登录信息
2015/10/20 PHP
详解WordPress开发中用于获取分类及子页面的函数用法
2016/01/08 PHP
PHP利用二叉堆实现TopK-算法的方法详解
2017/04/24 PHP
javascript之ESC(第二类混淆)
2007/05/06 Javascript
jquery调用asp.net 页面后台的实现代码
2011/04/27 Javascript
js获取时间(本周、本季度、本月..)
2013/11/22 Javascript
js中substr,substring,indexOf,lastIndexOf的用法小结
2013/12/27 Javascript
JavaScript运行机制之事件循环(Event Loop)详解
2014/10/10 Javascript
js实现多选项切换导航菜单的方法
2015/02/06 Javascript
深入讲解AngularJS中的自定义指令的使用
2015/06/18 Javascript
总结在前端排序中遇到的问题
2016/07/19 Javascript
用JS写的一个Ajax库(实例代码)
2016/08/06 Javascript
jQuery实现在HTML文档加载完毕后自动执行某个事件的方法
2017/05/08 jQuery
javascript 封装Date日期类实例详解
2017/05/28 Javascript
angular中实现li或者某个元素点击变色的两种方法
2017/07/27 Javascript
vue语法之拼接字符串的示例代码
2017/10/25 Javascript
react native 原生模块桥接的简单说明小结
2019/02/26 Javascript
详释JavaScript执行环境与执行栈
2019/04/02 Javascript
vue  elementUI 表单嵌套验证的实例代码
2019/11/06 Javascript
vue 路由meta 设置导航隐藏与显示功能的示例代码
2020/09/04 Javascript
js+canvas实现刮刮奖功能
2020/09/13 Javascript
微信小程序实现页面左右滑动
2020/11/16 Javascript
Python入门之三角函数atan2()函数详解
2017/11/08 Python
pycharm修改界面主题颜色的方法
2019/01/17 Python
python协程gevent案例 爬取斗鱼图片过程解析
2019/08/27 Python
Python 70行代码实现简单算式计算器解析
2019/08/30 Python
python3连接mysql获取ansible动态inventory脚本
2020/01/19 Python
使用Keras中的ImageDataGenerator进行批次读图方式
2020/06/17 Python
python 视频下载神器(you-get)的具体使用
2021/01/06 Python
上海雨人软件技术开发有限公司测试题
2015/07/14 面试题
连锁酒店店长职责范本
2014/02/13 职场文书
2019秋季运动会口号
2019/06/25 职场文书