详解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 相关文章推荐
Java中重定向输出流实现用文件记录程序日志
Jun 12 Python
Python列表list解析操作示例【整数操作、字符操作、矩阵操作】
Jul 25 Python
Python实现模拟登录网易邮箱的方法示例
Jul 05 Python
linux安装Python3.4.2的操作方法
Sep 28 Python
使用python画社交网络图实例代码
Jul 10 Python
pytorch获取vgg16-feature层输出的例子
Aug 20 Python
python GUI库图形界面开发之PyQt5图片显示控件QPixmap详细使用方法与实例
Feb 27 Python
python实现批量修改文件名
Mar 23 Python
如何让python的运行速度得到提升
Jul 08 Python
python实现文件+参数发送request的实例代码
Jan 05 Python
Django debug为True时,css加载失败的解决方案
Apr 24 Python
教你如何使用Python开发一个钉钉群应答机器人
Jun 21 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
一首老MP3,致敬WAR3经典
2021/03/08 魔兽争霸
基于数据库的在线人数,日访问量等统计
2006/10/09 PHP
php 信息采集程序代码
2009/03/17 PHP
php中运用http调用的GET和POST方法示例
2014/09/29 PHP
PHP使用mysql_fetch_object从查询结果中获取对象集的方法
2015/03/18 PHP
PHP直接修改表内容DataGrid功能实现代码
2015/09/24 PHP
图片连续滚动代码[兼容IE/firefox]
2009/06/11 Javascript
JSP中使用JavaScript动态插入删除输入框实现代码
2014/06/13 Javascript
javascript常用代码段搜集
2014/12/04 Javascript
JavaScript获取对象在页面中位置坐标的方法
2016/02/03 Javascript
AngularJS基础 ng-submit 指令简单示例
2016/08/03 Javascript
jQuery实现自动调用和触发某个事件的方法
2016/11/18 Javascript
JS实现复制功能
2017/03/01 Javascript
谈谈vue中mixin的一点理解
2017/12/12 Javascript
详解webpack运行Babel教程
2018/06/13 Javascript
vue组件开发之用户无限添加自定义填写表单的方法
2018/08/28 Javascript
python实现多线程采集的2个代码例子
2014/07/07 Python
python实现红包裂变算法
2016/02/16 Python
Python 装饰器深入理解
2017/03/16 Python
Python实现随机漫步功能
2018/07/09 Python
Python爬虫之网页图片抓取的方法
2018/07/16 Python
解决python通过cx_Oracle模块连接Oracle乱码的问题
2018/10/18 Python
Python中使用logging和traceback模块记录日志和跟踪异常
2019/04/09 Python
如何更优雅地写python代码
2019/07/02 Python
Python中变量的输入输出实例代码详解
2019/07/28 Python
如何通过Python3和ssl实现加密通信功能
2020/05/09 Python
CSS3线性渐变简单实现以及该属性在浏览器中的不同
2012/12/12 HTML / CSS
HTML5 3D书本翻页动画的实现示例
2019/08/28 HTML / CSS
英国评分最高的女性剃须刀订阅盒:FFS Beauty
2018/01/25 全球购物
MaBelle玛贝尔香港官网:香港钻饰连锁店
2019/09/09 全球购物
学生党员批评与自我批评
2014/10/15 职场文书
2015年元旦联欢晚会活动总结
2014/11/28 职场文书
公司庆典欢迎词
2015/01/26 职场文书
大专护理专业自荐信
2015/03/25 职场文书
辩论赛开场白大全(主持人+辩手)
2015/05/29 职场文书
贫困证明书范文
2015/06/16 职场文书