详解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中的RSS处理
Apr 13 Python
在Python中操作文件之seek()方法的使用教程
May 24 Python
python类中super()和__init__()的区别
Oct 18 Python
解决python 输出是省略号的问题
Apr 19 Python
对python中数据集划分函数StratifiedShuffleSplit的使用详解
Dec 11 Python
python中树与树的表示知识点总结
Sep 14 Python
python re模块匹配贪婪和非贪婪模式详解
Feb 11 Python
Django User 模块之 AbstractUser 扩展详解
Mar 11 Python
python:删除离群值操作(每一行为一类数据)
Jun 08 Python
Django rest framework分页接口实现原理解析
Aug 21 Python
python实现网络五子棋
Apr 11 Python
Python中re模块的元字符使用小结
Apr 07 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
用session做客户验证时的注意事项
2006/10/09 PHP
php快速url重写更新版[需php 5.30以上]
2010/04/25 PHP
PHP的魔术常量__METHOD__简介
2014/07/08 PHP
mac os快速切换多个PHP版本的方法
2017/03/07 PHP
php获取ip及网址的简单方法(必看)
2017/04/01 PHP
基于jquery的横向滚动条(滑动条)
2011/02/24 Javascript
JQuery+EasyUI轻松实现步骤条效果
2016/02/22 Javascript
angular2倒计时组件使用详解
2017/01/12 Javascript
JavaScript在控件上添加倒计时功能的实现代码
2017/07/04 Javascript
vue.js中父组件调用子组件的内部方法示例
2017/10/22 Javascript
微信小程序slider组件使用详解
2018/01/31 Javascript
详解JS函数stack size计算方法
2018/06/18 Javascript
jQuery Ajax实现Select多级关联动态绑定数据的实例代码
2018/10/26 jQuery
JS实现盒子跟着鼠标移动及键盘方向键控制盒子移动效果示例
2019/01/29 Javascript
jQuery Migrate 插件用法实例详解
2019/05/22 jQuery
React实现轮播效果
2020/08/25 Javascript
python中使用xlrd、xlwt操作excel表格详解
2015/01/29 Python
详解Python中open()函数指定文件打开方式的用法
2016/06/04 Python
怎样使用Python脚本日志功能
2016/08/14 Python
Python爬虫之模拟知乎登录的方法教程
2017/05/25 Python
django加载本地html的方法
2018/05/27 Python
python实现网站微信登录的示例代码
2019/09/18 Python
浅析python中while循环和for循环
2019/11/19 Python
Python+Dlib+Opencv实现人脸采集并表情判别功能的代码
2020/07/01 Python
keras中epoch,batch,loss,val_loss用法说明
2020/07/02 Python
Python tempfile模块生成临时文件和临时目录
2020/09/30 Python
HTML5实现Notification API桌面通知功能
2016/03/02 HTML / CSS
美国著名珠宝品牌之一:Jared The Galleria Of Jewelry
2016/10/01 全球购物
英国花园家具中心:Garden Furniture Centre
2017/08/24 全球购物
英国时尚配饰、珠宝和服装网站:KJ Beckett
2020/01/23 全球购物
土木工程专业推荐信
2014/02/19 职场文书
建筑工程专业大学生求职信
2014/04/23 职场文书
2014年学生工作总结
2014/11/20 职场文书
给男朋友的道歉短信
2015/05/12 职场文书
Python下opencv库的安装过程及问题汇总
2021/06/11 Python
Elasticsearch 批量操作
2022/04/19 Python