我试图从图像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。
答案 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。
[2] https://cloud.google.com/appengine/docs/standard/python/blobstore/