You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django结合Pillow生成贺卡图片无法存库及返回用户的问题

核心错误点
  • Pillow的Image.save()方法无返回值,直接将其返回值(None)赋值给ImageField字段,不可能存入有效图片
  • 视图未区分GET/POST请求,用户首次打开页面时就会尝试读取POST参数触发异常
  • 图片存储逻辑未走Django文件存储机制,直接将图片写入项目根目录,无法被Django识别访问
  • 模板未接收生成的贺卡实例,且ImageField字段渲染方式错误,无法正常展示图片
  • 代码中硬编码Windows绝对路径,兼容性极差,换运行环境就会报错

修复步骤

1. 补全媒体文件配置

首先在项目settings.py末尾添加媒体文件配置:

import os
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

再打开项目根路由文件urls.py,添加开发环境下的媒体文件访问路由:

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # 保留你原有的路由配置
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

2. 重写views.py逻辑

用内存流处理Pillow生成的图片,走Django官方文件存储API保存,同时区分请求方法:

from django.shortcuts import render
from .models import Name
from PIL import Image, ImageDraw, ImageFont
from django.core.files.base import ContentFile
from io import BytesIO
import os
from django.conf import settings

def Home(request):
    # GET请求直接返回空表单页面
    if request.method == "GET":
        return render(request, 'index.html')
    
    # POST请求处理贺卡生成
    name_input = request.POST.get('user_name', '').strip()
    if not name_input:
        return render(request, 'index.html', {'error': '请输入姓名'})
    
    # 用BASE_DIR拼接文件路径,替换硬编码的绝对路径
    base_img_path = os.path.join(settings.BASE_DIR, 'files', 'covers', 'mypic.png')
    font_path = os.path.join(settings.BASE_DIR, 'fonts', 'static', 'Cairo-Bold.ttf')
    
    # Pillow绘制贺卡
    img = Image.open(base_img_path)
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype(font_path, 40)
    draw.text((540,1020), name_input, font=font, fill=(237, 185, 117), anchor="ms")

    # 将生成的图片写入内存二进制流,无需提前落盘到本地
    img_buffer = BytesIO()
    img.save(img_buffer, format='PNG')
    img_buffer.seek(0) # 将流指针移回起始位置

    # 存入数据库
    card_instance = Name(massenger_name=name_input)
    img_filename = f"eid_card_{name_input}_{card_instance.action_time.strftime('%Y%m%d%H%M%S')}.png"
    card_instance.image.save(img_filename, ContentFile(img_buffer.read()), save=False)
    card_instance.save()

    # 把生成的贺卡实例传给模板
    return render(request, 'index.html', {'card': card_instance})

3. 修复模板渲染逻辑

修正表单属性和图片渲染代码,替换原index.html中对应部分:

<!DOCTYPE html>
<html lang="en" dir="rtl">
  <head>
    <meta charset="utf-8">
    <title>节日贺卡生成</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
    {% load static %}
    <link rel="stylesheet" href="{% static 'CSS/style.css' %}">
  </head>
  <body>
    <div id="form">
      <form class="row" method="POST" enctype="multipart/form-data">
        {% csrf_token %}
          <div class="">
            <input type="text" class="form-control" placeholder="أكتب أسمك (مثلا/أخوكم عبدالله العتيبي)" id="text_name" name="user_name" required>
          </div>
          <div class="col-auto">
            <button type="submit" class="btn btn-primary mb-3" id="button">生成贺卡</button>
          </div>
      </form>

      <!-- 生成成功后展示贺卡 -->
      {% if card %}
      <div class="result-area mt-4">
        <p>你的贺卡已生成:</p>
        <img src="{{ card.image.url }}" alt="{{ card.massenger_name }}的节日贺卡" class="img-fluid">
        <a href="{{ card.image.url }}" download class="btn btn-success mt-2">下载贺卡</a>
      </div>
      {% endif %}

      {% if error %}
      <p class="text-danger mt-2">{{ error }}</p>
      {% endif %}
    </div>
  </body>
</html>

额外注意事项

  • 确认项目根目录下media文件夹存在,且Django进程对该文件夹有读写权限
  • 原代码中输入框的type="textarea"是错误写法,单行文本输入使用type="text"即可
  • 图片文件名加了时间戳后缀,避免多用户同名提交导致文件覆盖

内容的提问来源于stack exchange,提问作者data data

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:36:27