问题描述
我正在使用Django创建一个Web服务,我希望该Web服务返回图像.我正在决定我的Web服务的基本体系结构.我在Google上绊倒后得出的结论是:
- 我应该将图像编码为base64格式后将图像存储在db中.
- 当直接碱基64解码字符串传输时,传输图像很容易.
但是我有一个问题如何使用django型号将bases64编码字符串存储在DB中?另外,如果您在我的基本体系结构中看到任何缺陷,请引导我.
我是Web Services和Django的新手
谢谢!
推荐答案
有些人指出,将DB用于存储图像不是一个好主意,但这不是真的.我的建议是将Django与 appengine blobstore Service 通过这种方式:
首先,创建一个 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映像模型:
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() ...
然后,您必须创建一个申请应用程序,以接收DJANGO请求以存储图像,将base64字符串转换为RAW并将其存储在斑点中.例如:
# 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)) ...
服务斑点
关键是函数 get_serving_url .来自Google文档:
如果您正在使用图像,则更有效,可能 不太昂贵的方法是使用app Engine使用get_serving_url 图像API而不是send_blob. get_serving_url函数让 您直接提供图像,而无需通过您的应用程序 发动机实例.
最后,通过将图像与Appengine一起使用,您可以使用调整大小和裁剪图像的出色功能(检查get_serving_url函数的文档),例如:
//将图像大小调整到32个像素(保留方面比例) http://your_app_id.app_id.appsspot.com/randomstromstringimageImageImageImageImageImageImageImageid=s32
希望它有帮助.祝你好运!
其他推荐答案
您可以将图像存储在 bytea字段中.
bytea数据类型允许存储二进制字符串. postgres documention link
django的较早版本不支持bytea字段,因此我一直在使用此库,称为 djorm -ext-pgbytea :
其他推荐答案
我这样做:
我的数据库是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;
要使您需要更新的视图,您需要创建而不是触发器:
on Update触发器的示例:
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字段存在. 这是您的选择.
我使用做,没有问题.正如我所说的,我是一个数据库侧的家伙:)
问题描述
I am using Django for creating one Web service and I want that web service to return Images. I am deciding the basic architecture of my Web service. What I came up to conclusion after stumbling on google is:
- I should store Images in DB after encoding them to Base64 format.
- Transferring the Images would be easy when directly Bases64 decoded string is transmitted.
But I have one issue how can I store bases64 encoded string in DB using Django models? Also, if you see any flaw in my basic architecture please guide me.
I am new to Web services and Django
Thanks!!
推荐答案
Some people states that using DB for storing images is not a good idea but that's no true. My advice is to use Django with AppEngine Blobstore Service in this way:
First, create a Django Custom Storage (or pick one from someone else like this one):
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()
This custom storage can receive Django images, convert them to Base64 strings and send them to your AppEngine Blobstore Service application (via xmlrpc for example).
Then, create a Django Image model:
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() ...
Then, you have to create an AppEngine application for receiving Django requests for storing images, transform Base64 strings to raw and store them in a Blob. For example:
# 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)) ...
Serving a blob
The key is the function get_serving_url. From Google docs:
If you are serving images, a more efficient and potentially less-expensive method is to use get_serving_url using the App Engine Images API rather than send_blob. The get_serving_url function lets you serve the image directly, without having to go through your App Engine instances.
Finally, by serving images with AppEngine, you can use the awesome feature of resize and crop images (check the documentation of get_serving_url function), for example:
// Resize the image to 32 pixels (aspect-ratio preserved) http://your_app_id.appspot.com/randomStringImageId=s32
Hope it helps. Good luck!
其他推荐答案
You can store image in bytea field.
The bytea data type allows storage of binary strings. Postgres Documentation Link
The earlier version of Django does not support bytea field, so I have been using this library called djorm-ext-pgbytea:
其他推荐答案
I do it like this:
My database is postgres and I usualy ignore djago migrations so I have more flexibility to do things on the database side. I do creating a view with the proper triggers to encode and decode the image at the database side so for example:
for this table:
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)
)
I create a view like this one:
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;
To make the view updatable of coarse you need to create the instead of triggers:
example of the on update trigger:
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;
and
CREATE TRIGGER do_brand_update INSTEAD OF UPDATE ON sales.v_brand_base64 FOR EACH ROW EXECUTE PROCEDUE sales.brand_view_update();
At django side:
the model:
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"'
And the view like:
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)
It is probably the not best way but it works
the image is saved on the database in a bytea field and you can use the property b64_logo_image like a field to render the image on your templates something like {{object.b64_logo_image}}
About to record images at database that is why blob and bytea fields exists. It is your choice.
I use do do it and I have no problems. As I said I am more a database side guy :)