如何在AppEngine中存储base64编码的图像?

时间:2018-03-19 17:53:53

标签: python google-app-engine google-cloud-datastore blobstore

我试图从图像data-uri对象创建一个blobstore条目,但是我遇到了问题。

基本上,我通过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转换为图像以存储为blob?

我还希望此Handler返回在blobstore中创建的映像的URL。

2 个答案:

答案 0 :(得分:2)

有几种方法可以查看您的问题以及您发布的示例代码,因为您正在混合策略和技术,所以您需要的内容有点混乱。

POST base64到_ah/upload/...

您的服务使用create_upload_url()为您的客户制作一次性上传网址/会话。您的客户端对该URL发出POST并且数据从不接触您的服务(没有HTTP请求大小限制,没有花费CPU时间来处理POST)。 App Engine内部" blob服务"接收该POST并将正文保存为Blob存储中的Blob。然后,App Engine将控制权交还给您编写的BlobstoreUploadHandler课程中的服务,然后您可以确定如何响应成功的POST。对于示例/教程,PhotoUploadHandler将客户端重定向到刚刚上传的照片。

客户端的POST必须编码为multipart/mixed,并使用示例HTML <form>中显示的字段。

multipart表单可以使用可选参数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测试的完整多部分表单,基于示例中使用的字段。我在Is there a way to pass the content of a file to 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并使用云存储。请按照Setting Up Google Cloud Storage的文档进行操作,然后修改您的服务,为您选择的存储桶创建上传网址:

create_upload_url(gs_bucket_name=...)

它比这更复杂,但是在Blobstore文档中阅读使用Blobstore API和Google Cloud Storage 这一部分可以指向正确的方向。

将base64直接发布到您的服务/处理程序

有点像您在原始帖子中编码,您的服务从您的客户端收到POST,然后您决定是否需要操作图像以及您要将其存储在何处(数据存储区,Blob存储区,云存储区)。

如果你需要操纵图像,那么使用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)

答案 1 :(得分:0)

图像对象(https://cloud.google.com/appengine/docs/standard/python/refdocs/google.appengine.api.images)不是数据存储区实体,因此没有密钥。您需要将图像实际保存到blobstore [2]或Google Cloud Storage [1],然后为您的图像获取服务URL。

[1] https://cloud.google.com/appengine/docs/standard/python/googlecloudstorageclient/setting-up-cloud-storage

[2] https://cloud.google.com/appengine/docs/standard/python/blobstore/