详解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实现批量获取指定文件夹下的所有文件的厂商信息
Sep 28 Python
Python中enumerate()函数编写更Pythonic的循环
Mar 06 Python
Python列表生成式与生成器操作示例
Aug 01 Python
Python深拷贝与浅拷贝用法实例分析
May 05 Python
python隐藏类中属性的3种实现方法
Dec 19 Python
使用Python制作新型冠状病毒实时疫情图
Jan 28 Python
Python新手学习标准库模块命名
May 29 Python
pytorch查看模型weight与grad方式
Jun 24 Python
django创建css文件夹的具体方法
Jul 31 Python
python读取xml文件方法解析
Aug 04 Python
Python3+Django get/post请求实现教程详解
Feb 16 Python
Python中的变量与常量
Nov 11 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
如何实现给定日期的若干天以后的日期
2006/10/09 PHP
PHP Pear 安装及使用
2009/03/19 PHP
destoon实现商铺管理主页设置增加新菜单的方法
2014/06/26 PHP
浅析PHP文件下载原理
2014/12/25 PHP
php等比例缩放图片及剪切图片代码分享
2016/02/13 PHP
js和jquery批量绑定事件传参数一(新猪猪原创)
2010/06/23 Javascript
JavaScript 在网页上单击鼠标的地方显示层及关闭层
2012/12/30 Javascript
正则表达式中特殊符号及正则表达式的几种方法总结(replace,test,search)
2013/11/26 Javascript
js的alert样式如何更改如背景颜色
2014/01/22 Javascript
json字符串之间的相互转换示例代码
2014/08/21 Javascript
js正则表达式匹配数字字母下划线等
2015/04/14 Javascript
详解AngularJs中$resource和restfu服务端数据交互
2016/09/21 Javascript
JavaScript reduce和reduceRight详解
2016/10/24 Javascript
JavaScript编写一个贪吃蛇游戏
2017/03/09 Javascript
ionic使用angularjs表单验证(模板验证)
2018/12/12 Javascript
vue单页面在微信下只能分享落地页的解决方案
2019/04/15 Javascript
JS实现的简单tab切换功能完整示例
2019/06/20 Javascript
[01:30]DOTA2上海特锦赛现场采访 Loda倾情献唱
2016/03/25 DOTA
[02:04]2016国际邀请赛中国区预选赛VG.R晋级之路
2016/07/01 DOTA
wxpython中利用线程防止假死的实现方法
2014/08/11 Python
python使用socket进行简单网络连接的方法
2015/04/29 Python
Python调用C++程序的方法详解
2017/01/24 Python
Python实现矩阵转置的方法分析
2017/11/24 Python
Python数据结构之栈、队列的实现代码分享
2017/12/04 Python
Python+OpenCV实现车牌字符分割和识别
2018/03/31 Python
python安装本地whl的实例步骤
2019/10/12 Python
python3 正则表达式基础廖雪峰
2020/03/25 Python
python多线程和多进程关系详解
2020/12/14 Python
利用Python实现自动扫雷小脚本
2020/12/17 Python
Python3.9.1中使用split()的处理方法(推荐)
2021/02/07 Python
微信小程序实现可实时改变转速的css3旋转动画实例代码
2018/09/11 HTML / CSS
全球性的在线婚纱礼服工厂:27dress.com
2019/03/21 全球购物
大学生实习介绍信
2015/05/05 职场文书
西游降魔篇观后感
2015/06/15 职场文书
二手手机买卖合同范本(2019年版)
2019/10/28 职场文书
解决tk mapper 通用mapper的bug问题
2021/06/16 Java/Android