【问题标题】:How do I store a base64-encoded image in AppEngine?如何在 AppEngine 中存储 base64 编码的图像?
【发布时间】:2018-08-28 09:29:02
【问题描述】:

我正在尝试从图像数据 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?

我还希望这个处理程序返回在 blobstore 中创建的图像的 URL。

【问题讨论】:

  • 你的ImageItem 定义/类在哪里?
  • @ZacharyYoung 抱歉,我之前编辑的内容有点过多并删掉了。我已经添加了 ImageItem 定义回来
  • 我有一个可以直接上传到 Blobstore 的解决方案;或让您的服务处理传入的图像。

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


【解决方案1】:

有几种方法可以查看您的问题和您发布的示例代码,由于您混合了策略和技术,因此您需要的内容有点混乱。

将 base64 发布到 _ah/upload/...

您的服务使用 create_upload_url() 为您的客户端创建一次性上传 URL/会话。您的客户端对该 URL 进行 POST,并且数据永远不会触及您的服务(没有 HTTP 请求大小限制,没有 CPU 时间花费在处理 POST 上)。 App Engine 内部“blob 服务”接收该 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 测试的完整的多部分表单,基于示例中使用的字段。我在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 的替代方案,如果您想节省一点存储成本或需要在不使用 API 的情况下共享数据,则可以使用云存储。按照Setting Up Google Cloud Storage 的文档进行操作,然后修改您的服务,为您选择的存储桶创建上传 URL:

create_upload_url(gs_bucket_name=...)

这比这要复杂一些,但是阅读 Blobstore 文档中的将 Blobstore API 与 Google 云存储结合使用部分会让您找到正确的方向。

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

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

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

【讨论】:

  • 谢谢扎卡里。我正在尝试这个。只需检查数据存储中将使用多少存储 blob。
  • 刚刚看到,我可以更新它以使用云存储。我现在就试试!
  • @user791793 我很好奇,这些对您和您的要求有用吗?
【解决方案2】:

图像对象 (https://cloud.google.com/appengine/docs/standard/python/refdocs/google.appengine.api.images) 不是 Datastore 实体,因此它没有键。您需要将图片实际保存到 blobstore[2] 或 Google Cloud Storage[1],然后获取图片的服务网址。

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

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

【讨论】:

  • 谢谢吉姆。我一直在关注 blobstore 的指南——您的链接 [2],但是该指南从上传字段中获取图像。我正在读取数据 uri,然后需要转换为图像。所以我知道我需要保存它。问题是如何:)
  • 在链接 [2] 中,有一个如何手动写入 GCS 的示例 (cloud.google.com/appengine/docs/standard/python/blobstore/…)。无论如何,您应该将 data-uri 中的数据写入 GCS,然后从 thatl 获取服务 url
猜你喜欢
  • 2019-01-21
  • 2016-09-18
  • 2023-04-10
  • 1970-01-01
  • 2018-12-20
  • 2020-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多