Django 图片上传upload_to路径指定失效的问题记录

初始方法一:

疑虑:model使用upload_to自定义路径方法失效,指定路径也失效。最后以Views中指定MEDIA_URL和MEDIA_ROOT做拼接,并且自行判断并建立文件夹,手动chunk保存。不完美的解决。

model:    看avatar字段的upload_to

import uuid
from django.db import models
from django.contrib.auth.models import AbstractUser


class SysUser(AbstractUser):
    """
    用户扩展表,用于替换auth的user类,并添加头像和昵称。
    """
    uid = models.BigAutoField(primary_key=True)
    nickname = models.CharField(max_length=32, verbose_name="昵称")
    # 使用 models.ImageField 需要安装 Pillow ,cmd命令:pip install Pillow
    avatar = models.ImageField(verbose_name="头像", upload_to="avatar11")

    class Meta:
        # 数据库显示的表名
        db_table = "sys_user"

form:

from django.forms import Form
from django.forms import widgets
from django.forms import fields

class EditUserForm(Form):
    """..."""
    e_avatar = fields.ImageField(
        required=False,
        widget=widgets.FileInput(attrs={"hidden": "hidden"})
    )
    """..."""

Views:

def upload_user_avatar(request):
    """用户上传头像操作"""
    if request.method == "POST":
        user_id = request.user.uid        # 只是当前登录用户
        ret = {'state': True, 'error': None, 'data': None}
        # user_id = request.POST.get("uid", None)
        avatar = request.FILES.get("avatar", None)

        # 如果文件夹不存在,则建立
        dir_path = os.path.join(MEDIA_ROOT, "users", "{}_{}".format(request.user.uid, request.user.username), "avatar")
        if not os.path.exists(os.path.join(dir_path)):
            os.makedirs(os.path.join(dir_path))
        avatar_path = os.path.join(dir_path, avatar.name)
        try:
            with open(avatar_path, 'wb+') as img:
                for chunk in avatar.chunks():
                    img.write(chunk)
                print("文件接收成功")
            img_url = os.path.join("users", "{}_{}".format(request.user.uid, request.user.username), "avatar")
            SysUser.objects.filter(uid=user_id).update(avatar=img_url)
            ret["data"] = "头像更新成功"
        except Exception as e:
            ret["state"] = False
            ret["data"] = "头像更新失败,请确认图片格式"
            print(e)
        return HttpResponse(json.dumps(ret))

html:

<div class="form-group text-center">
    <label>
        {# request.user.avatar  是读取实例的avatar字段,也就是不加MEDIA_URL前缀的 #}
        {# request.user.avatar.url  是读取实例的avatar字段了,并且添加MEDIA_URL作为前缀的 #}
        <img id="avatar-img" src="{{ request.user.avatar.url }}" width="200px" height="200px">
        {{ edit_user_form.e_avatar }}
    </label>
</div>

 

  • SysUser.objects.filter(uid=user_id).update(avatar=img_url)
  • 这句话执行结束后,只保存文件名,无法使用upload_to指定的路径。
  • 页面加载时,直接使用拼接后路径。

初始方法二:

TMD,一个保存方式的问题,竟然能啰啰嗦嗦这么久。以后以此方法操作。

model:不变

views:

def upload_user_avatar(request):
    """用户上传头像操作"""
    if request.method == "POST":
        user_id = request.user.uid        # 只是当前登录用户
        ret = {'state': True, 'error': None, 'data': None}
        avatar = request.FILES.get("avatar", None)

        try:
            user_obj = SysUser.objects.filter(uid=user_id).first()
            user_obj.avatar = avatar
            user_obj.save()
            ret["data"] = "头像更新成功"
        except Exception as e:
            ret["state"] = False
            ret["data"] = "头像更新失败,请确认图片格式"
            print(e)
        return HttpResponse(json.dumps(ret))
  • user_obj.save()
  • 使用这种保存方式的好处:
  1. 会自动保存文件路径到数据库
  2. 会自动保存文件到指定路径
  3. 必要时会自动创建目录。

目前最终方法:

model:

import uuid
import os
from django.db import models
from django.contrib.auth.models import AbstractUser
"""
python-cmd : makemigrations
python-cmd : migrate
"""


def user_directory_path(instance, filename):
    """用户上传文件时的自定义操作"""
    ext = filename.split('.')[-1]                           # 获取上传的文件名的后缀
    filename = '{0}.{1}'.format(uuid.uuid4().hex[:8], ext)  # 修改文件名,谨防重名。(文档说重名后,会自动增加后缀)
    sub_folder = 'avatar'
    # 日后如果对上传文件的类型进行归档时可以COPY这个做参考。
    # if ext.lower() in ["jpg", "png", "gif", "ico"]:
    #    sub_folder = "avatar"
    # if ext.lower() in ["pdf", "docx"]:
    #     sub_folder = "document"
    return os.path.join("users", "%s-%s" % (instance.user.uid, instance.user.username), sub_folder, filename)


class SysUser(AbstractUser):
    """
    用户扩展表,用于替换auth的user类,并添加头像和昵称。
    """
    uid = models.BigAutoField(primary_key=True)
    nickname = models.CharField(max_length=32, verbose_name="昵称")
    # 使用 models.ImageField 需要安装 Pillow ,cmd命令:pip install Pillow
    avatar = models.ImageField(verbose_name="头像", upload_to=user_directory_path)

    class Meta:
        # 数据库显示的表名
        db_table = "sys_user"

form:

from django.forms import Form
from django.forms import widgets
from django.forms import fields

class EditUserForm(Form):
    """..."""
    e_avatar = fields.ImageField(
        required=False,
        widget=widgets.FileInput(attrs={"hidden": "hidden"})
    )
    """页面操作该元素时,使用的ajax方式,所以仅对此项进行操作。未使用EditUserForm做验证等操作。"""
    """..."""

views:

def upload_user_avatar(request):
    """用户上传头像操作"""
    if request.method == "POST":
        user_id = request.user.uid        # 只是当前登录用户
        ret = {'state': True, 'error': None, 'data': None}
        # user_id = request.POST.get("uid", None)   # 如果以后有需求修改其他人的信息时,再从页面获取。
        avatar = request.FILES.get("avatar", None)

        try:
            user_obj = SysUser.objects.filter(uid=user_id).first()
            user_obj.avatar = avatar
            user_obj.save()
            ret["data"] = "头像更新成功"
        except Exception as e:
            ret["state"] = False
            ret["data"] = "头像更新失败,请确认图片格式"
            print(e)
        return HttpResponse(json.dumps(ret))

html:

<div class="form-group text-center">
    <label>
        {# request.user.avatar  是读取实例的avatar字段,也就是不加MEDIA_URL前缀的 #}
        {# request.user.avatar.url  是读取实例的avatar字段了,并且添加MEDIA_URL作为前缀的 #}
        <img id="avatar-img" src="{{ request.user.avatar.url }}" width="200px" height="200px">
        {{ edit_user_form.e_avatar }}
    </label>
</div>

JS:

// 上传文件按钮(label里的图片)点击事件
$('#id_e_avatar').on('change',function () {

	// 文件上传之前在本地加载预览。
	{
		// 获取用户最后一次选择的图片
		var choose_file=$(this)[0].files[0];
		// 创建一个新的FileReader对象,用来读取文件信息
		var reader=new FileReader();
		// 读取用户上传的图片的路径
		reader.readAsDataURL(choose_file);
		// 读取完毕之后,将图片的src属性修改成用户上传的图片的本地路径
		reader.onload=function () {
			 $("#avatar-img").attr("src",reader.result)
		}
	}

	// 使用FormData格式,将文件上传至服务器
	formdata = new FormData();
	//formdata.append('uid',$('#id_e_id').val());
	formdata.append("avatar",$("#id_e_avatar")[0].files[0]);

	$.ajax({
		type: "POST",
		url: "/account/upload_user_avatar",
		data:formdata,
		processData:false,	//上传文件时需要如此设置1
		contentType:false,	//上传文件时需要如此设置2
		success:function (data) {
			data = JSON.parse(data);
			console.log(data);
			alert(data.data)	// 偷懒了,哈哈
         }
	});
});

media_url映射:

from django.urls import path, re_path, include
from django.views.generic.base import RedirectView
from django.views.static import serve    # 映射media_root路径,可以通过浏览器访问的操作-1
from django.contrib.auth.decorators import login_required
from WXH_DJANGOWEB import settings
from utils.required import required

urlpatterns = [
    path('account/', include("account.urls")),
    path('blog/', include("blog.urls")),
    re_path(r"^media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT, })
    # 映射media_url,可以通过浏览器访问的操作-2
]

至此,记录完毕。回家吃饭。坑爹的保存方式…… 愿同志们绕坑而行,用不遇坑。

转载于:https://my.oschina.net/asktao/blog/3004672

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值