详解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记录程序运行时间的三种方法
Jul 14 Python
Python hashlib模块用法实例分析
Jun 12 Python
python range()函数取反序遍历sequence的方法
Jun 25 Python
python微信聊天机器人改进版(定时或触发抓取天气预报、励志语录等,向好友推送)
Apr 25 Python
Python实现数据结构线性链表(单链表)算法示例
May 04 Python
用Python配平化学方程式的方法
Jul 20 Python
python3 map函数和filter函数详解
Aug 26 Python
详解Python3 pandas.merge用法
Sep 05 Python
python 上下文管理器及自定义原理解析
Nov 19 Python
python tkinter 设置窗口大小不可缩放实例
Mar 04 Python
python 批量下载bilibili视频的gui程序
Nov 20 Python
Python使用永中文档转换服务
May 06 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文件上传问题汇总(文件大小检测、大文件上传处理)
2015/12/24 PHP
Document对象内容集合(比较全)
2010/09/06 Javascript
JS中的this变量的使用介绍
2013/10/21 Javascript
JavaScript给url网址进行encode编码的方法
2015/03/18 Javascript
javascript中Math.random()使用详解
2015/04/15 Javascript
JavaScript编程中布尔对象的基本使用
2015/10/25 Javascript
基于javascript制作经典传统的拼图游戏
2016/03/22 Javascript
Node.js插件安装图文教程
2016/05/06 Javascript
微信小程序中用WebStorm使用LESS
2017/03/08 Javascript
nodejs多版本管理总结
2018/04/03 NodeJs
JS实现监控微信小程序的原理
2018/06/15 Javascript
vue项目强制清除页面缓存的例子
2019/11/06 Javascript
JS实现电脑虚拟键盘打字测试
2020/06/24 Javascript
[01:07:02]DOTA2-DPC中国联赛 正赛 iG vs PSG.LGD BO3 第三场 2月26日
2021/03/11 DOTA
python笔记(1) 关于我们应不应该继续学习python
2012/10/24 Python
python实现排序算法
2014/02/14 Python
Python基于scapy实现修改IP发送请求的方法示例
2017/07/08 Python
Python实现的概率分布运算操作示例
2017/08/14 Python
用PyInstaller把Python代码打包成单个独立的exe可执行文件
2018/05/26 Python
Python实现的读取文件内容并写入其他文件操作示例
2019/04/09 Python
python实现Dijkstra算法的最短路径问题
2019/06/21 Python
python获取Linux发行版名称
2019/08/30 Python
python利用openpyxl拆分多个工作表的工作簿的方法
2019/09/27 Python
使用python和pygame制作挡板弹球游戏
2019/12/03 Python
python轮询机制控制led实例
2020/05/03 Python
Python几种常见算法汇总
2020/06/02 Python
详解python方法之绑定方法与非绑定方法
2020/08/17 Python
新加坡航空官方网站:Singapore Airlines
2016/10/13 全球购物
Otel.com:折扣酒店预订
2017/08/24 全球购物
阳光体育:Sunny Sports(购买露营和远足设备)
2018/08/07 全球购物
销售部主管岗位职责
2013/12/18 职场文书
开工仪式主持词
2014/03/20 职场文书
党员个人对照检查材料
2014/10/01 职场文书
2016年感恩教师节活动总结
2016/04/01 职场文书
创业计划书之婴幼儿游泳馆
2019/09/11 职场文书
Netty结合Protobuf进行编解码的方法
2021/06/26 Java/Android