详解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 03 Python
微信跳一跳python代码实现
Jan 05 Python
Python模块WSGI使用详解
Feb 02 Python
Flask Web开发入门之文件上传(八)
Aug 17 Python
详解Python函数式编程—高阶函数
Mar 29 Python
Django Sitemap 站点地图的实现方法
Apr 29 Python
在python中,使用scatter绘制散点图的实例
Jul 03 Python
numpy 返回函数的上三角矩阵实例
Nov 25 Python
python中threading开启关闭线程操作
May 02 Python
Python3实现建造者模式的示例代码
Jun 28 Python
详解python UDP 编程
Aug 24 Python
python元组拆包实现方法
Feb 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 生成静态页面的办法与实现代码详细版
2010/02/15 PHP
file_get_contents("php://input", "r")实例介绍
2013/07/01 PHP
PHP的邮件群发系统phplist配置方法详细总结
2016/03/30 PHP
PHP 自动加载类原理与用法实例分析
2020/04/14 PHP
用js实现多域名不同文件的调用方法
2007/01/12 Javascript
js事件(Event)知识整理
2012/10/11 Javascript
让低版本浏览器支持input的placeholder属性(js方法)
2013/04/03 Javascript
js跑马灯代码(自写)
2013/04/17 Javascript
你必须知道的JavaScript 变量命名规则详解
2013/05/07 Javascript
自己编写的类似JS的trim方法
2013/10/09 Javascript
javascript break指定标签打破多层循环示例
2014/01/20 Javascript
jQuery获取动态生成的元素示例
2014/06/15 Javascript
seajs加载jquery时提示$ is not a function该怎么解决
2015/10/23 Javascript
基于js实现二级下拉联动
2016/12/17 Javascript
javascript删除html标签函数cIsHTML
2017/01/09 Javascript
使用vue.js写一个tab选项卡效果
2017/03/25 Javascript
Vue列表页渲染优化详解
2017/07/24 Javascript
从parcel.js打包出错到选择nvm的全部过程
2018/01/23 Javascript
微信小程序如何像vue一样在动态绑定类名
2018/04/17 Javascript
WebSocket的通信过程与实现方法详解
2018/04/29 Javascript
js统计页面上每个标签的数量实例代码
2018/05/29 Javascript
微信小程序中上传图片并进行压缩的实现代码
2018/08/28 Javascript
小程序扫描普通链接二维码跳转小程序指定界面方法
2019/05/07 Javascript
原生JS与JQ获取元素的区别详解
2020/02/13 Javascript
vue路由分文件拆分管理详解
2020/08/13 Javascript
React实现全选功能
2020/08/25 Javascript
OpenLayer3自定义测量控件MeasureTool
2020/09/28 Javascript
python 3.0 模拟用户登录功能并实现三次错误锁定
2017/11/01 Python
Centos7下源码安装Python3 及shell 脚本自动安装Python3的教程
2020/03/07 Python
使用Matplotlib绘制不同颜色的带箭头的线实例
2020/04/17 Python
巴西最好的男鞋:Rafarillo
2018/05/25 全球购物
日本小田急百货官网:Odakyu
2018/07/19 全球购物
金融专业推荐信
2013/11/14 职场文书
运动会邀请函范文
2014/02/06 职场文书
初三学习计划书范文
2014/04/30 职场文书
sql注入教程之类型以及提交注入
2021/08/02 MySQL