将图像作为头像存储到数据存储中:可能吗?

1 投票
3 回答
536 浏览
提问于 2025-04-16 08:03

有没有人处理过收到邮件中的附件?我在想,用户可以把图片作为附件发送,而不是自己上传,这样我就可以用这个附件上传到数据存储中。

文档中有关于发送附件的内容,但我找不到关于接收附件的文档。这一页说

附件

消息的文件附件,以一个包含两个值的元组列表表示,每个附件对应一个元组。

每个元组的第一个元素是文件名,第二个元素是文件内容。

附件文件必须是允许的文件类型之一,文件名必须以与类型对应的扩展名结尾。有关允许的类型和文件扩展名的列表,请参见概述:附件。

我觉得这也和发送邮件有关。

我有这段代码可以把图片保存到数据存储中:

class AvatarSave(webapp.RequestHandler):
    def post(self):
        q = User.all()
        q.filter("userEmail =", emailAddress)
        qTable = q.fetch(10)
        if qTable:
            for row in qTable:
                avatar = images.resize(self.request.get("img"), 50, 50)
                row.avatar = db.Blob(avatar)
            db.put(qTable)
        else:
            self.response.out.write("user not found")

        self.redirect('/')

直观上看,似乎用message.attachment而不是"img"就可以解决问题。

avatar = images.resize(self.request.get(message.attachment), 50, 50)

你觉得怎么样?谢谢。


更新2(根据Nick Johnson的评论更新的新代码)

class Register(InboundMailHandler):
    def receive(self, message):
        senderEmail = message.sender
        emailTuple = parseaddr(senderEmail)
        emailUserName = emailTuple[0]
        emailAddress = emailTuple[1]
        newAvatar = db.Blob(images.resize(goodDecode(message.attachments[0][1]), 50, 50))        
        newUser = User(userEmail=emailAddress,
                       userName=emailUserName,
                       avatar=newAvatar)
                   
        db.put(newUser)

更新1 问题解决了:

为了记录下来,也为了有同样问题的人请注意,消息的属性attachments而不是attachment

message.attachment

会出现AttributeError错误

AttributeError: 'InboundEmailMessage' object has no attribute 'attachment'

而对象message.attachment看起来是这样的:

[('portrait.png', <EncodedPayload payload=#8461006914571150170 encoding=base64>)]

所以提取<EncodedPayload payload=#8461006914571150170 encoding=base64>部分的正确方法是

avatar = images.resize(goodDecode(message.attachments[0][1]), 50, 50)

我之前发布的原始代码是

avatar = images.resize(goodDecode(message.attachments[1]), 50, 50)

显然这不管用。

再次感谢jesmithRobert Kluin的回答。


更新0(关于jesmith的回答)

在我的情况下,我从用户上传的表单中获取一张图片"img",并像这样写入数据存储:

       for row in qTable:
            avatar = images.resize(self.request.get("img"), 50, 50)
            row.avatar = db.Blob(avatar)    
        db.put(qTable)
        self.redirect('/')
    else:
        logging.info("else user not found")
        self.redirect('/user-not-found')
        

在你的代码中,这对应于这一部分,我相信:

try:
    if hasattr(message, "attachment"):
        for a in message.attachments:
            msg.attachmentNames.append(a[0])
            msg.attachmentContents.append(append(db.Blob(goodDecode(a[1])))
        msg.put()        
except:
    logging.exception("exception decoding attachments in email from %s" % message.sender)

假设在我的情况下只有一个附件;我该如何获取附件的数据部分?

message.attachment[1]吗?

avatar = images.resize(message.attachment[1], 50, 50)
            

message.attachment[1]附件的数据部分吗?

谢谢!

3 个回答

1

查看一下处理接收邮件这一部分的文档,它解释了附件的属性。

InboundEmailMessage对象包含了一些属性,可以用来访问其他邮件字段:

  • attachments是一个文件附件的列表,可能是空的。列表中的每个值都是一个包含两个元素的元组:文件名和文件内容。

所以你需要的东西大概是这样的:

for file_name, data in message.attachments:
    # add some tests to check that the
    # file-type is correct and that the
    # data-size is "OK".
    avatar = db.Blob(data)

你需要决定当用户附加了多个图片时该如何处理。这段代码会简单地使用最后一个附件。

1

是的,关于你的后续问题:

是 message.attachment[1] 吗?

没错,但你需要对它进行解码。否则,你得到的只是一大堆看起来像是乱码的ASCII字符(在电子邮件中,附件总是以一种7位、短行、1975年安全格式编码,比如base64)。

avatar = images.resize(goodDecode(message.attachment[1]), 50, 50)

而且别忘了在周围加上很多try-catch逻辑,因为在GAE中处理邮件的部分很容易出现很多错误。

2

这是我用来处理来邮件的一个片段:

   bodies = message.bodies(content_type='text/html')
    allBodies = u"";
    for body in bodies:
      allBodies = allBodies + u"\n" + unicode(goodDecode(body[1]), errors="ignore")
    if not allBodies:
      bodies = message.bodies(content_type='text/plain')
      for body in bodies:
        allBodies = allBodies + u"\n" + unicode(goodDecode(body[1]), errors="ignore")

    msg = EmailMessageModel()
...fill in various stuff...
    msg.sender = message.sender
    msg.date = datetime.datetime.now()
    msg.message = allBodies
    # Calling put() before dealing with attachments because it seems like that could throw various exceptions
    msg.put()
    event.email = True
    event.put()
    event.project.email = True
    event.project.put()
    # attachments is a list of element pairs containing file names and contents.

    try:
      if hasattr(message, 'attachments'):
        for a in message.attachments:
          msg.attachmentNames.append(a[0])
          msg.attachmentContents.append(db.Blob(goodDecode(a[1])))
        msg.put()
    except:
      logging.exception("Exception decoding attachments in email from %s" % message.sender)

需要注意的是,goodDecode是我自己写的一个函数,因为底层的GAE解码有个bug(它把所有内容都变成小写,这样会搞乱base64编码的文本):

def goodDecode(encodedPayload):
  if not hasattr(encodedPayload, 'encoding'):
    return encodedPayload
  encoding = encodedPayload.encoding
  payload = encodedPayload.payload
  if encoding and encoding.lower() != '7bit':
    payload = payload.decode(encoding)
  return payload

现在可能不再需要这个函数了,因为我相信他们已经修复了那个bug。

在我的情况下,我把附件存入数据库:

class EmailMessageModel(db.Model):
....various stuff...
  sender = db.StringProperty()
  date = db.DateTimeProperty()
  message = db.TextProperty()
  attachmentNames = db.StringListProperty()
  attachmentContents = db.ListProperty(db.Blob)

当我想要显示这封邮件时,我使用的是:

<h2>{{ e.sender }} {{ e.date|date:"M j, Y f A " }} GMT</h2>
<p>From: {{ e.sender }}<br/>Date: {{ e.date|date:"M j, Y f A" }} GMT ({{ e.date|timesince }} ago)<br/>Subject: {{ e.subject }}</p>
{% if e.attachmentNames %}
<p>Attachments: 
  {% for a in e.attachmentNames %}
<a href="/admin/attachment?email={{ e.key }}&index={{ forloop.counter0 }}" target="_blank">{{ a }}</a>
  {% endfor %}
</p>
{% endif %}
<div style='background-color: white'>{{ e.message }}</div>

附件处理的部分是:

class AttachmentHandler(webapp.RequestHandler):
  def get(self):
    email = EmailMessageModel.get(self.request.get('email'))
    index = self.request.get('index')
    if index:
      index = int(index)
      filename = email.attachmentNames[index]
      self.response.headers['Content-Type'] = str(mimetypes.guess_type(filename)[0]) or 'application/octet-stream'
      self.response.out.write(email.attachmentContents[index])

(所以,基本上,我是让浏览器自己决定怎么处理这些附件。)

希望这对你有帮助!

撰写回答