详解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 相关文章推荐
可用于监控 mysql Master Slave 状态的python代码
Feb 10 Python
Python操作MongoDB详解及实例
May 18 Python
python学习教程之Numpy和Pandas的使用
Sep 11 Python
通过Py2exe将自己的python程序打包成.exe/.app的方法
May 26 Python
Python爬虫之正则表达式基本用法实例分析
Aug 08 Python
浅谈Pycharm调用同级目录下的py脚本bug
Dec 03 Python
pyecharts在数据可视化中的应用详解
Jun 08 Python
python制作微博图片爬取工具
Jan 16 Python
Python实现京东抢秒杀功能
Jan 25 Python
浅谈Python列表嵌套字典转化的问题
Apr 07 Python
Python极值整数的边界探讨分析
Sep 15 Python
如何利用Python实现n*n螺旋矩阵
Jan 18 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学习之数组的定义和填充
2011/04/17 PHP
JObj预览一个JS的框架
2008/03/13 Javascript
告诉大家什么是JSON
2008/06/10 Javascript
JS的数组的扩展实例代码
2008/07/09 Javascript
jQuery.getScript加载同域JS的代码
2012/02/13 Javascript
基于jQuery实现的百度导航li拖放排列效果,即时更新数据库
2012/07/31 Javascript
如何使用jquery动态加载js,css文件实现代码
2013/04/03 Javascript
CSS3实现动态背景登录框的代码
2015/07/28 Javascript
一道JS前端闭包面试题解析
2015/12/25 Javascript
详解webpack+es6+angular1.x项目构建
2017/05/02 Javascript
js实现放大镜特效
2017/05/18 Javascript
详解vue express启动数据服务
2017/07/05 Javascript
微信公众号平台接口开发 获取微信服务器IP地址方法解析
2019/08/14 Javascript
nodemon实现Typescript项目热更新的示例代码
2019/11/19 Javascript
vue element-ui实现input输入框金额数字添加千分位
2019/12/29 Javascript
编写同时兼容Python2.x与Python3.x版本的代码的几个示例
2015/03/30 Python
在Python中处理字符串之isdecimal()方法的使用
2015/05/20 Python
Python基于Tkinter的HelloWorld入门实例
2015/06/17 Python
通过数据库对Django进行删除字段和删除模型的操作
2015/07/21 Python
详解Python之unittest单元测试代码
2018/01/24 Python
Python3对称加密算法AES、DES3实例详解
2018/12/06 Python
创建Django项目图文实例详解
2019/06/06 Python
python爬虫豆瓣网的模拟登录实现
2019/08/21 Python
python3获取url文件大小示例代码
2019/09/18 Python
浅谈Python3识别判断图片主要颜色并和颜色库进行对比的方法
2019/10/25 Python
Python 用__new__方法实现单例的操作
2020/12/11 Python
马德里竞技官方网上商店:Atletico Madrid Shop
2019/03/31 全球购物
俄罗斯最大的在线手表商店:Bestwatch.ru
2020/01/11 全球购物
工作失误检讨书范文大全
2014/01/13 职场文书
董事长秘书职责
2014/01/31 职场文书
创新比赛获奖感言
2014/02/13 职场文书
幼儿园中秋节活动反思
2014/02/16 职场文书
股东协议书
2014/04/14 职场文书
建设单位项目负责人任命书
2014/06/06 职场文书
分析SQL窗口函数之取值窗口函数
2022/04/21 Oracle
使用CSS实现六边形的图片效果
2022/08/05 HTML / CSS