详解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中元类用法实例
Oct 10 Python
利用Python中SocketServer 实现客户端与服务器间非阻塞通信
Dec 15 Python
python flask中静态文件的管理方法
Mar 20 Python
python selenium自动上传有赞单号的操作方法
Jul 05 Python
Python 判断奇数偶数的方法
Dec 20 Python
Django的models中on_delete参数详解
Jul 16 Python
Python 自动登录淘宝并保存登录信息的方法
Sep 04 Python
python使用opencv实现马赛克效果示例
Sep 28 Python
python绘制雪景图
Dec 16 Python
将keras的h5模型转换为tensorflow的pb模型操作
May 25 Python
Python tkinter制作单机五子棋游戏
Sep 14 Python
python接口测试返回数据为字典取值方式
Feb 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
PHP6 mysql连接方式说明
2009/02/09 PHP
php中关于codeigniter的xmlrpc的类在进行数据交换时的类型问题
2011/07/03 PHP
如何用php获取文件名后缀
2013/06/09 PHP
IIS6.0 开启Gzip方法及PHP Gzip函数分享
2014/06/08 PHP
php表单提交与$_POST实例分析
2015/01/26 PHP
PHP 输出缓冲控制(Output Control)详解
2016/08/25 PHP
jQuery 研究心得 取得属性的值
2007/11/30 Javascript
JavaScipt基本教程之前言
2008/01/16 Javascript
extjs 分页使用jsp传递数据示例
2014/07/29 Javascript
Javascript中3个需要注意的运算符
2015/04/02 Javascript
js实现选中复选框文字变色的方法
2015/08/14 Javascript
json对象转为字符串,当做参数传递时加密解密的实现方法
2016/06/29 Javascript
js模仿微信朋友圈计算时间显示几天/几小时/几分钟/几秒之前
2017/04/27 Javascript
jQuery UI 实例讲解 - 日期选择器(Datepicker)
2017/09/18 jQuery
bootstrap table方法之expandRow-collapseRow展开或关闭当前行数据
2020/08/09 Javascript
详解webpack4升级指南以及从webpack3.x迁移
2018/06/12 Javascript
webpack4 处理CSS的方法示例
2018/09/03 Javascript
记一次webapck4 配置文件无效的解决历程
2018/09/19 Javascript
使用JavaScript解析URL的方法示例
2019/03/01 Javascript
Vue实现背景更换颜色操作
2020/07/17 Javascript
基于VSCode调试网页JavaScript代码过程详解
2020/07/20 Javascript
JavaScript实现页面高亮操作提示和蒙板
2021/01/04 Javascript
对Pandas MultiIndex(多重索引)详解
2018/11/16 Python
python实现五子棋人机对战游戏
2020/03/25 Python
Django Celery异步任务队列的实现
2019/07/24 Python
Pycharm创建文件时自动生成文件头注释(自定义设置作者日期)
2020/11/24 Python
阿联酋电子产品购物网站:Menakart
2017/09/15 全球购物
行政专员岗位职责范本
2014/08/26 职场文书
高中毕业典礼演讲稿
2014/09/09 职场文书
县委常委班子对照检查材料思想汇报
2014/09/28 职场文书
严以律己专题学习研讨会发言材料
2015/11/09 职场文书
2019年公司快递收发管理制度模板
2019/11/20 职场文书
Python基础之hashlib模块详解
2021/05/06 Python
详解nodejs内置模块
2021/05/06 NodeJs
人民币符号
2022/02/17 杂记
python神经网络 使用Keras构建RNN训练
2022/05/04 Python