Как сохранить изображение в кодировке base64 в AppEngine?
Я пытаюсь создать запись в хранилище блогов из объекта data-uri, но я застреваю.
По сути, я публикую через ajax data-uri как текст, пример полезной нагрузки:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPA...
Я пытаюсь получить эту полезную нагрузку с помощью следующего обработчика. Я предполагаю, что мне нужно конвертировать data-uri
вернуться в образ перед сохранением? Поэтому я использую библиотеку PIL.
Мой обработчик Python выглядит следующим образом:
import os
import urllib
import webapp2
from google.appengine.ext.webapp import template
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
from google.appengine.api import images
class ImageItem(db.Model):
section = db.StringProperty(required=False)
description = db.StringProperty(required=False)
img_url = db.StringProperty()
blob_info = blobstore.BlobReferenceProperty()
when = db.DateTimeProperty(auto_now_add=True)
#Paste upload handler
class PasteUpload(webapp2.RequestHandler):
def post(self):
from PIL import Image
import io
import base64
data = self.request.body
#file_name = data['file_name']
img_data = data.split('data:image/png;base64,')[1]
#Convert base64 to jpeg bytes
f = Image.open(io.BytesIO(base64.b64decode(img_data)))
img = ImageItem(description=self.request.get('description'), section=self.request.get('section') )
img.blob_info = f.key()
img.img_url = images.get_serving_url( f.key() )
img.put()
Это, вероятно, все виды неправильно. Я получаю следующую ошибку при публикации:
img.blob_info = f.key()
AttributeError: 'PngImageFile' object has no attribute 'key'
Что я здесь не так делаю? Есть ли более простой способ сделать это? Я думаю, мне не нужно конвертировать data-uri
в изображение для хранения в виде капли?
Я также хочу, чтобы этот обработчик возвращал URL-адрес изображения, созданного в blobstore.
2 ответа
Есть несколько способов просмотреть ваш вопрос и пример кода, который вы разместили, и это немного сбивает с толку то, что вам нужно, потому что ваши стратегии и технологии микширования.
POST base64 для_ah/upload/...
Ваш сервис использует create_upload_url()
сделать одноразовую загрузку URL/ сессии для вашего клиента. Ваш клиент создает POST для этого URL, и данные никогда не затрагивают вашу службу (без ограничений размера HTTP-запроса, без затрат времени ЦП на обработку POST). Внутренняя "служба BLOB-объектов" App Engine получает этот POST и сохраняет тело как Blob в Blobstore. Затем App Engine передает управление вашему сервису в BlobstoreUploadHandler
Класс, который вы пишете, а затем вы можете определить, как вы хотите ответить на успешный POST. В случае с примером / учебником, PhotoUploadHandler
перенаправляет клиента на только что загруженную фотографию.
Этот POST от вашего клиента должен быть закодирован как multipart/mixed
и используйте поля, показанные в примере HTML <form>
,
Составная форма может принимать необязательный параметр, Content-Transfer-Encoding
и внутренний обработчик App Engine правильно декодирует данные base64. От blob_upload.py
:
base64_encoding = (form_item.headers.get('Content-Transfer-Encoding') ==
'base64')
...
if base64_encoding:
blob_file = cStringIO.StringIO(base64.urlsafe_b64decode(blob_file.read()))
...
Вот полная многочастная форма, которую я протестировал с помощью cURL, основываясь на полях, использованных в примере. Я узнал, как это сделать в. Есть ли способ передать содержимое файла в curl?:
myconfig.txt:
header = "Content-length: 435"
header = "Content-type: multipart/mixed; boundary=XX
data-binary = "@myrequestbody.txt"
myrequestbody.txt:
--XX
Content-Disposition: form-data; name="file"; filename="test.gif"
Content-Type: image/gif
Content-Transfer-Encoding: base64
R0lGODdhDwAPAIEAAAAAzMzM/////wAAACwAAAAADwAPAAAIcQABCBxIsODAAAACAAgAIACAAAAiSgwAIACAAAACAAgAoGPHACBDigwAoKTJkyhTqlwpQACAlwIEAJhJc6YAAQByChAAoKfPn0CDCh1KtKhRAAEAKF0KIACApwACBAAQIACAqwECAAgQAIDXr2DDAggIADs=
--XX
Content-Disposition: form-data; name="submit"
Submit
--XX--
а потом беги как:
curl --config myconfig.txt "http://127.0.0.1:8080/_ah/upload/..."
Вам нужно будет создать / макет многочастной формы в вашем клиенте.
Вы также не можете использовать Blobstore и Cloud Storage, если хотите / нуждаетесь. Следуйте документации по настройке Google Cloud Storage, а затем измените свой сервис, чтобы создать URL для загрузки по вашему выбору:
create_upload_url(gs_bucket_name=...)
Это немного сложнее, чем просто это, но чтение раздела Использование API-интерфейса Blobstore с Google Cloud Storage в документе Blobstore поможет вам в правильном направлении.
POST base64 непосредственно к вашему сервису / обработчику
Вроде того, как вы кодировали в исходном посте, ваш сервис получает POST от вашего клиента, и вы затем решаете, нужно ли вам манипулировать изображением и где вы хотите его сохранить (Datastore, Blobstore, Cloud Storage).
Если вам нужно манипулировать изображением, тогда хорошо использовать PIL:
from io import BytesIO
from PIL import Image
from StringIO import StringIO
data = self.request.body
#file_name = data['file_name']
img_data = data.split('data:image/png;base64,')[1]
# Decode base64 and open as Image
img = Image.open(BytesIO(base64.b64decode(img_data)))
# Create thumbnail
img.thumbnail((128, 128))
# Save img output as blob-able string
output = StringIO()
img.save(output, format=img.format)
img_blob = output.getvalue()
# now you choose how to save img_blob
Если вам не нужно манипулировать изображением, просто остановитесь на b64decode()
:
img_blob = base64.b64decode(img_data)
Объект изображения ( https://cloud.google.com/appengine/docs/standard/python/refdocs/google.appengine.api.images) не является сущностью хранилища данных, поэтому у него нет ключа. Вам действительно нужно сохранить изображение в blobstore[2] или Google Cloud Storage[1], а затем получить URL-адрес для вашего изображения.
[2] https://cloud.google.com/appengine/docs/standard/python/blobstore/