详解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 15 Python
简单实现python聊天程序
Apr 01 Python
利用Pandas 创建空的DataFrame方法
Apr 08 Python
python3+PyQt5实现自定义流体混合窗口部件
Apr 24 Python
Django中使用Celery的方法示例
Nov 29 Python
详解Python下载图片并保存本地的两种方式
May 15 Python
Python内置random模块生成随机数的方法
May 31 Python
python画蝴蝶曲线图的实例
Nov 21 Python
通过Python实现一个简单的html页面
May 16 Python
浅谈pytorch 模型 .pt, .pth, .pkl的区别及模型保存方式
May 25 Python
matplotlib bar()实现百分比堆积柱状图
Feb 24 Python
Python爬取酷狗MP3音频的步骤
Feb 26 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单链表的实现代码
2016/07/05 PHP
thinkPHP模板引擎用法示例
2016/12/08 PHP
PHP错误提示It is not safe to rely on the system……的解决方法
2019/03/25 PHP
根据分辩率调用不同的CSS.
2007/01/08 Javascript
自己动手开发jQuery插件教程
2011/08/25 Javascript
js实现日历可获得指定日期周数及星期几示例分享(js获取星期几)
2014/03/14 Javascript
页面装载js及性能分析方法介绍
2014/03/21 Javascript
浅谈Sticky组件的改进实现
2016/03/22 Javascript
jQuery表单验证简单示例
2016/10/17 Javascript
Angular 2应用的8个主要构造块有哪些
2016/10/17 Javascript
Mongoose学习全面理解(推荐)
2017/01/21 Javascript
AngulerJS学习之按需动态加载文件
2017/02/13 Javascript
javascript checkbox/radio onchange不能兼容ie8处理办法
2017/06/13 Javascript
jQuery实现点击关注和取消功能
2017/07/03 jQuery
微信小程序之蓝牙的链接
2017/09/26 Javascript
Ionic学习日记实现验证码倒计时
2018/02/08 Javascript
原生JS检测CSS3动画是否结束的方法详解
2019/01/27 Javascript
Vue 中文本内容超出规定行数后展开收起的处理的实现方法
2019/04/28 Javascript
微信小程序页面渲染实现方法
2019/11/06 Javascript
[12:29]2018国际邀请赛 开幕秀
2018/08/22 DOTA
python 不关闭控制台的实现方法
2011/10/23 Python
django模型中的字段和model名显示为中文小技巧分享
2014/11/18 Python
python版学生管理系统
2018/01/10 Python
django 多数据库配置教程
2018/05/30 Python
pygame游戏之旅 按钮上添加文字的方法
2018/11/21 Python
解决在Python编辑器pycharm中程序run正常debug错误的问题
2019/01/17 Python
使用Keras预训练模型ResNet50进行图像分类方式
2020/05/23 Python
Python装饰器如何实现修复过程解析
2020/09/05 Python
美特斯邦威官方商城:邦购网
2016/10/13 全球购物
Abe’s of Maine:自1979以来销售相机和电子产品
2016/11/21 全球购物
服装店营销方案
2014/03/10 职场文书
舞蹈专业大学生职业规划范文
2014/03/12 职场文书
2015年党员自我剖析材料
2014/12/17 职场文书
办公室主任岗位职责
2015/01/31 职场文书
nginx服务器的下载安装与使用详解
2021/08/02 Servers
vue打包时去掉所有的console.log
2022/04/10 Vue.js