简体   繁体   English

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

[英]How do I store a base64-encoded image in AppEngine?

I'm trying to create a blobstore entry from an image data-uri object, but am getting stuck.我正在尝试从图像数据 uri 对象创建一个 blobstore 条目,但我被卡住了。

Basically, I'm posting via ajax the data-uri as text, an example of the payload:基本上,我通过 ajax 将 data-uri 作为文本发布,这是有效负载的示例:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPA...

I'm trying to receive this payload with the following handler.我正在尝试使用以下处理程序接收此有效负载。 I'm assuming I need to convert the data-uri back into an image before storing?我假设我需要在存储之前将data-uri转换回图像? So am using the PIL library.所以我正在使用 PIL 库。

My python handler is as follows:我的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()

This is likely all kinds of wrong.这很可能是各种错误。 I get the following error when posting:发布时出现以下错误:

img.blob_info = f.key()
AttributeError: 'PngImageFile' object has no attribute 'key'

What am I doing wrong here?我在这里做错了什么? Is there an easier way to do this?有没有更简单的方法来做到这一点? I'm guessing I don't need to convert the data-uri into an image to store as a blob?我猜我不需要将data-uri转换为图像以存储为 blob?

I also want this Handler to return the URL of the image created in the blobstore.我还希望此处理程序返回在 blobstore 中创建的图像的 URL。

There are a couple of ways to view your question and the sample code you posted, and it's a little confusing what you need because you are mixing strategies and technologies.有几种方法可以查看您的问题和您发布的示例代码,并且您需要什么有点令人困惑,因为您正在混合策略和技术。

POST base64 to _ah/upload/... POST base64 到_ah/upload/...

Your service uses create_upload_url() to make a one-time upload URL/session for your client.您的服务使用create_upload_url()为您的客户端创建一次性上传 URL/会话。 Your client makes a POST to that URL and the data never touches your service (no HTTP-request-size restrictions, no CPU-time spent handling the POST).您的客户端对该 URL 进行 POST 并且数据永远不会触及您的服务(没有 HTTP 请求大小限制,没有 CPU 时间用于处理 POST)。 An App Engine internal "blob service" receives that POST and saves the body as a Blob in the Blobstore. App Engine 内部“blob 服务”接收该 POST 并将正文保存为 Blobstore 中的 Blob。 App Engine then hands control back to your service in the BlobstoreUploadHandler class you write and then you can determine how you want to respond to the successful POST.然后,App Engine 将控制权交还给您编写的BlobstoreUploadHandler类中的服务,然后您可以确定要如何响应成功的 POST。 In the case of the example/tutorial, PhotoUploadHandler redirects the client to the photo that was just uploaded.在示例/教程的情况下, PhotoUploadHandler将客户端重定向到刚刚上传的照片。

That POST from your client must be encoded as multipart/mixed and use the fields shown in the example HTML <form> .来自客户端的 POST 必须编码为multipart/mixed并使用示例 HTML <form>显示的字段。

The multipart form can take the optional parameter, Content-Transfer-Encoding , and the App Engine internal handler will properly decode base64 data.多部分表单可以采用可选参数Content-Transfer-Encoding ,App Engine 内部处理程序将正确解码 base64 数据。 From blob_upload.py :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()))

...

Here's a complete multipart form I tested with cURL, based on the fields used in the example.这是我使用 cURL 测试的完整多部分表单,基于示例中使用的字段。 I found out how to do this over at Is there a way to pass the content of a file to 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 : 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--

and then run like:然后像这样运行:

curl --config myconfig.txt "http://127.0.0.1:8080/_ah/upload/..."

You'll need to create/mock-up the multipart form in your client.您需要在客户端中创建/模拟多部分表单。

Also, as an alternative to Blobstore, you can use Cloud Storage if you want to save a little on storage costs or have some need to share the data without your API.此外,作为 Blobstore 的替代方案,如果您想节省一点存储成本或需要在没有 API 的情况下共享数据,则可以使用 Cloud Storage。 Follow the documentation for Setting Up Google Cloud Storage , and then modify your service to create the upload URL for your bucket of choice:按照设置 Google Cloud Storage的文档,然后修改您的服务以创建您选择的存储桶的上传 URL:

create_upload_url(gs_bucket_name=...)

It's a little more complicated than just that, but reading the section Using the Blobstore API with Google Cloud Storage in the Blobstore document will get you pointed in the right direction.这比这要复杂一些,但阅读 Blobstore 文档中的将Blobstore API 与 Google Cloud Storage 结合使用部分将使您找到正确的方向。

POST base64 directly to your service/handler将 base64 直接发布到您的服务/处理程序

Kind of like you coded in the original post, your service receives the POST from your client and you then decide if you need to manipulate the image and where you want to store it (Datastore, Blobstore, Cloud Storage).有点像您在原始帖子中编码,您的服务从您的客户端接收 POST,然后您决定是否需要操作图像以及您想要存储它的位置(数据存储、Blobstore、云存储)。

If you need to manipulate the image, then using PIL is good:如果您需要操作图像,那么使用 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

If you don't need to manipulate the image, just stop at b64decode() :如果您不需要操作图像,只需在b64decode()处停止:

img_blob = base64.b64decode(img_data)

An image object ( https://cloud.google.com/appengine/docs/standard/python/refdocs/google.appengine.api.images ) isn't a Datastore entity, so it has no key.图像对象 ( https://cloud.google.com/appengine/docs/standard/python/refdocs/google.appengine.api.images ) 不是数据存储区实体,因此它没有密钥。 You need to actually save the image to blobstore[2] or Google Cloud Storage[1] then get a serving url for your image.您需要将图像实际保存到 blobstore[2] 或 Google Cloud Storage[1],然后获取图像的服务 URL。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在不写入磁盘的情况下将 numpy 数组编码为 base64 编码的 PNG? - How do I encode a numpy array to a base64-encoded PNG without writing to disk? 在ReportLab生成的PDF中包含base64编码的图像 - Including base64-encoded image in ReportLab-generated PDF 如何从发布的base64编码图像创建MongoDB / mongoengine ImageField? - How to Create a MongoDB/mongoengine ImageField from POSTed base64-encoded image? 如何反序列化base64编码的数据并将其与DRF一起使用 - How to deserialize base64-encoded data and use it with DRF base64编码图像; binascii.Error: 无效的 base64 编码 - base64 encoding image; binascii.Error: Invalid base64-encoded 如何使用python刮取base 64图像编码并存储在数据库中 - How to scrape encoded with base 64 image and store in database using python Python,获取图像对象的 base64 编码的 MD5 哈希 - Python, get base64-encoded MD5 hash of an image object 如何在Tkinter标签中使用base64编码的图像字符串? - How do I use the base64 encoded image string in Tkinter label? 将base64编码的字符串转换为十六进制int - Convert base64-encoded string into hex int PEM 格式的 base64 编码公钥的序列化 - Serialization of a base64-encoded public key in PEM format
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM