13

我正在使用 Django 创建一个 Web 服务,并且我希望该 Web 服务返回图像。我正在决定我的 Web 服务的基本架构。我在偶然发现谷歌后得出的结论是:

  1. 在将图像编码为 Base64 格式后,我应该将图像存储在数据库中。
  2. 当直接传输 Bases64 解码字符串时,传输图像会很容易。

但是我有一个问题,如何使用 Django 模型将 bases64 编码的字符串存储在 DB 中?另外,如果您发现我的基本架构中有任何缺陷,请指导我。

我是 Web 服务和 Django 的新手

谢谢!!

4

4 回答 4

12

有人说使用数据库存储图像不是一个好主意,但事实并非如此。我的建议是以这种方式将 Django 与AppEngine Blobstore 服务一起使用:

首先,创建一个Django 自定义存储(或者像这样从其他人那里选择一个):

from django.core.files.storage import Storage 

class AppEngineBlobStorage(Storage):

    def exists(self, name):
       ...   

    def size(self, name):
       ...

    def url(self, name):
       ...

    def delete(self, name):
       ...

    def listdir(self, path):
       raise NotImplementedError()

此自定义存储可以接收 Django 图像,将它们转换为 Base64 字符串并将它们发送到您的 AppEngine Blobstore 服务应用程序(例如通过 xmlrpc)。

然后,创建一个 Django Image 模型:

from django.db import models
from django.db.models.fields.files import ImageField
from .storage import AppEngineBlobStorage

class Photo(models.Model):
    caption = models.CharField(max_length=64, blank=True)
    blob = ImageField(
        upload_to='BlobStorage',
        storage=AppEngineBlobStorage(),
        max_length=255,
        blank=False,
    )
    serving_url = models.URLField()
    ...

然后,您必须创建一个 AppEngine 应用程序来接收 Django 存储图像的请求,将 Base64 字符串转换为原始字符串并将它们存储在 Blob 中。例如:

# coding=utf-8

from __future__ import with_statement

import webapp2

from base64 import b64decode
from StringIO import StringIO
from xmlrpcserver import XmlRpcServer

from google.appengine.api import files
from google.appengine.api import images
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers


class ImageRpcServer(object):

    def upload(self, meta, photo, mime_type):
        data = b64decode(photo)
        file_name = files.blobstore.create(mime_type=mime_type)
        with files.open(file_name, 'a') as f:
            f.write(data)
        files.finalize(file_name)
        key = files.blobstore.get_blob_key(file_name)
        return str(key)

    def serving_url(self, meta, key):
        return images.get_serving_url(blobstore.BlobKey(key))

      ...

服务一个 blob

关键是函数get_serving_url。来自谷歌文档:

如果您要提供图像,一种更高效且成本可能更低的方法是使用 App Engine Images API 而不是 send_blob 来使用 get_serving_url。get_serving_url 函数可让您直接提供图像,而无需通过您的 App Engine 实例。

最后,通过使用 AppEngine 提供图像,您可以使用调整图像大小和裁剪图像的强大功能(查看get_serving_url函数文档),例如:

// 将图像大小调整为 32 像素(保留长宽比) http://your_app_id.appspot.com/randomStringImageId=s32

希望能帮助到你。祝你好运!

于 2013-09-13T04:37:42.553 回答
0

您可以将图像存储在bytea 字段中

bytea数据类型允许存储二进制字符串。Postgres 文档链接

早期版本的 Django 不支持 bytea 字段,所以我一直在使用这个名为djorm-ext-pgbytea 的库

于 2013-09-11T18:45:48.900 回答
0

我这样做:

我的数据库是 postgres,我通常会忽略 djago 迁移,因此我可以更灵活地在数据库方面做事。我确实使用适当的触发器创建了一个视图,以在数据库端对图像进行编码和解码,例如:

对于这个表:

CREATE TABLE sales.brand (
   id serial not null, 
   brand_name character varying(100), 
   is_national_brand boolean default true not null, 
   general_comments text, 
   logo_image bytea
   CONSTRAINT brand_pk PRIMARY KEY (id)

)

我创建了一个这样的视图:

CREATE OR REPLACE VIEW sales.v_brand_base64 AS 
SELECT brand.id,
    brand.brand_name,
    brand.is_national_brand,
    brand.general_comments,
    logo_image,
    encode(brand.logo_image, 'base64'::text) AS base64_logo_image
FROM sales.brand;

要使粗略的视图可更新,您需要创建而不是触发器:

更新触发器示例:

CREATE OR REPLACE FUNCTION sales.brand_view_update()
   RETURNS trigger
   LANGUAGE plpgsql
AS 
$BODY$
BEGIN

    UPDATE sales.brand SET 
        id = NEW.id, 
        brand_name = NEW.brand_name, 
        is_national_brand = NEW.is_national_brand, 
        general_comments = NEW.general_comments,
        logo_image = decode(NEW.base64_logo_image, 'base64')
    WHERE     
        id = OLD.id;
    
    RETURN NEW;
END;
$BODY$
VOLATILE
SECURITY INVOKER
CALLED ON NULL INPUT
COST 100;

CREATE TRIGGER do_brand_update INSTEAD OF UPDATE
ON sales.v_brand_base64
FOR EACH ROW 
EXECUTE PROCEDUE sales.brand_view_update();

在 django 方面:

该模型:

class Brand(models.Model):
    id = models.AutoField(primary_key=True)
    brand_name = models.CharField(max_length=100)
    is_national_brand = models.BooleanField()
    general_comments = models.TextField(blank=True, null=True)
    logo_image = models.ImageField(null=True)
    base64_logo_image = models.TextField(blank=True, null=True)

    def b64_logo_image(self):
        base64enc = self.base64_logo_image
        return format_html('<img style="width: 100%" display: block;" src="data:image/bmp;base64,{}">', base64enc)

    def __str__(self):
        return self.brand_name

    @property
    def detail_fields(self):
        # (0:value, 1:href, 2:header, 3:size, 4:list, 5:detail, )
        return (
            (self.brand_name, safe_self_rev(self, 'brand_detail'), 'Brand Name', 10, True, True, ),
            (self.is_national_brand, None, 'National Brand', 5, True, True, ),
            (self.general_comments, None, 'Comments', 5, True, True, ),
            (self.b64_logo_image, None, 'Logo', 5, True, True, ),
        )

    class Meta:
        managed = False
        db_table = '"sales"."v_brand_base64"'

像这样的观点:

class BrandUpdateView(UpdateView):

    model = Brand
    fields = ['brand_name', 'is_national_brand', 'general_comments', 'logo_image', ]

    template_name = "sales/brand_edit.html"

    def get_success_url(self):
        if self.object.id is None:
            return reverse('index')
        else:
            return reverse('brand_detail', args=[self.object.id])

    def form_valid(self, form):
        if form.is_valid():
            brand = form.save(commit=False)
            logo_image = form.cleaned_data['logo_image'].file.read()
            brand.logo_image = None
            logo_base64 = base64.encodebytes(logo_image).decode();
            brand.base64_logo_image = logo_base64
            brand.save()
        return super().form_valid(form)

这可能不是最好的方法,但它有效

图像保存在数据库中的 bytea 字段中,您可以像字段一样使用属性 b64_logo_image 在模板上呈现图像,例如 {{object.b64_logo_image}}

即将在数据库中记录图像,这就是 blob 和 bytea 字段存在的原因。这是你的选择。

我用做它,我没有问题。正如我所说,我更像是一个数据库方面的人:)

于 2022-01-08T12:48:31.687 回答
0

我认为最好的方法是将“主文件”存储在项目的媒体路径中,并将文件的地址(文件路径)保存在模型中。这样你就不需要转换....

于 2022-01-14T11:58:02.307 回答