详解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文件读写并使用mysql批量插入示例分享(python操作mysql)
Feb 17 Python
Python爬虫实现百度图片自动下载
Feb 04 Python
python生成多个只含0,1元素的随机数组或列表的实例
Nov 12 Python
Python实现 版本号对比功能的实例代码
Apr 18 Python
python使用openCV遍历文件夹里所有视频文件并保存成图片
Jan 14 Python
Python实现病毒仿真器的方法示例(附demo)
Feb 19 Python
关于Python Tkinter Button控件command传参问题的解决方式
Mar 04 Python
Pytorch通过保存为ONNX模型转TensorRT5的实现
May 25 Python
Python celery原理及运行流程解析
Jun 13 Python
简单了解如何封装自己的Python包
Jul 08 Python
python 进程池pool使用详解
Oct 15 Python
python数字图像处理:图像简单滤波
Jun 28 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下MAIL的另一解决方案
2006/10/09 PHP
一个图形显示IP的PHP程序代码
2007/10/19 PHP
PHP基础学习之流程控制的实现分析
2013/04/28 PHP
基于Discuz security.inc.php代码的深入分析
2013/06/03 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(六)
2014/06/23 PHP
PHP爬虫之百万级别知乎用户数据爬取与分析
2016/01/22 PHP
用函数式编程技术编写优美的 JavaScript
2006/11/25 Javascript
javascript Array.remove() 数组删除
2009/08/06 Javascript
利用JS重写Cognos右键菜单的实现代码
2010/04/11 Javascript
javascript的数据类型、字面量、变量介绍
2012/05/23 Javascript
js模仿hover的具体实现代码
2013/12/30 Javascript
js结合正则实现国内手机号段校验
2015/06/19 Javascript
jQuery实现固定在网页顶部的菜单效果代码
2015/09/02 Javascript
jQuery配合coin-slider插件制作幻灯片效果的流程解析
2016/05/13 Javascript
限制文本框只能输入数字||只能是数字和小数点||只能是整数和浮点数
2016/05/27 Javascript
JS实用的带停顿的逐行文本循环滚动效果实例
2016/11/23 Javascript
AngularJS模仿Form表单提交的实现代码
2016/12/08 Javascript
Vue.js watch监视属性知识点总结
2019/11/11 Javascript
如何将Node.js中的回调转换为Promise
2020/11/10 Javascript
[02:26]2016国际邀请赛8月3日开战 中国军团出征西雅图
2016/08/02 DOTA
python的迭代器与生成器实例详解
2014/07/16 Python
python进程类subprocess的一些操作方法例子
2014/11/22 Python
Python全排列操作实例分析
2018/07/24 Python
python构建指数平滑预测模型示例
2019/11/21 Python
Python第三方包PrettyTable安装及用法解析
2020/07/08 Python
python中plt.imshow与cv2.imshow显示颜色问题
2020/07/16 Python
详解css3中的伪类before和after常见用法
2020/11/17 HTML / CSS
Html5之title吸顶功能
2018/06/04 HTML / CSS
联想美国官方商城:Lenovo美国
2017/06/19 全球购物
金融专业大学生自我评价
2014/01/09 职场文书
七一表彰活动方案
2014/01/18 职场文书
酒店销售经理岗位职责
2014/01/31 职场文书
会计工作决心书
2014/03/11 职场文书
测量JavaScript函数的性能各种方式对比
2021/04/27 Javascript
原生JavaScript实现简单五子棋游戏
2021/06/28 Javascript
Node-Red实现MySQL数据库连接的方法
2021/08/07 MySQL