【问题标题】:Receiving Email Attachments in App Engine Python errors on Unicode Text File在 Unicode 文本文件上的 App Engine Python 错误中接收电子邮件附件
【发布时间】:2012-05-28 13:54:08
【问题描述】:

我有一些代码可以解析电子邮件并找到附件,然后将它们作为 db.BlobProperties 存储到数据存储中(以后可能会更改为 Blobstore)。问题是当我发送一个 UTF8 编码的文本文件时,它会产生一个错误。

代码基本上保存文件并返回一个密钥,该密钥转换为字符串,然后存储在父电子邮件实体中。如您所见,我对文件进行解码,然后将其存储为 blob。我已经发送了很多附件,并且此代码适用于除使用 Unicode 编码的文本之外的所有内容。有一个更好的方法吗?如何处理 Unicode 文本附件?

代码片段

    my_file = []
    my_list = []
    if hasattr(mail_message, 'attachments'):
        file_name = ""
        file_blob = ""
        for filename, filecontents in mail_message.attachments:
            file_name = filename
            file_blob = filecontents.decode()
            my_file.append(file_name)
            my_list.append(str(store_file(self, file_name, file_blob)))

存储文件

def store_file(self, file_name, file_blob):
    new_file = myblob(file_name = file_name, 
                      file_blob = file_blob)
    return new_file.put()

我尝试在上面使用file_blob = str(file_blob) 无济于事。这只会破坏代码,并且文件永远不会被存储。

Unicode 文本文件的日志 1

Property file_blob must be convertible to a Blob instance (Blob() argument should be str instance, not unicode)
Traceback (most recent call last):
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 1530, in __call__
    rv = self.router.dispatch(request, response)
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 1278, in default_dispatcher
    return route.handler_adapter(request, response)
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 1102, in __call__
    return handler.dispatch()
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 572, in dispatch
    return self.handle_exception(e, self.app.debug)
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 570, in dispatch
    return method(*args, **kwargs)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/webapp/mail_handlers.py", line 65, in post
    self.receive(mail.InboundEmailMessage(self.request.body))
  File "/base/data/home/apps/s~ae-baseapp/1.359073377819595139/controllers/InboundHandler.py", line 51, in receive
    file_list.append(str(store_file(self, file_name, file_blob)))
  File "/base/data/home/apps/s~ae-baseapp/1.359073377819595139/models/MyModel.py", line 63, in store_file
    file_blob = file_blob)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 974, in __init__
    prop.__set__(self, value)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 614, in __set__
    value = self.validate(value)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 2780, in validate
    (self.name, self.data_type.__name__, err))
BadValueError: Property file_blob must be convertible to a Blob instance (Blob() argument should be str instance, not unicode)

删除 filecontents.decode() 并将其替换为仅文件内容的日志 2。

Property file_blob must be convertible to a Blob instance (Blob() argument should be str instance, not EncodedPayload)
Traceback (most recent call last):
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 1530, in __call__
    rv = self.router.dispatch(request, response)
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 1278, in default_dispatcher
    return route.handler_adapter(request, response)
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 1102, in __call__
    return handler.dispatch()
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 572, in dispatch
    return self.handle_exception(e, self.app.debug)
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.5.1/webapp2.py", line 570, in dispatch
    return method(*args, **kwargs)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/webapp/mail_handlers.py", line 65, in post
    self.receive(mail.InboundEmailMessage(self.request.body))
  File "/base/data/home/apps/s~ae-baseapp/1.359097282640216691/controllers/InboundHandler.py", line 57, in receive
    file_list.append(str(store_file(self, file_name, file_blob)))
  File "/base/data/home/apps/s~ae-baseapp/1.359097282640216691/models/MyModel.py", line 64, in store_file
    file_blob = file_blob)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 974, in __init__
    prop.__set__(self, value)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 614, in __set__
    value = self.validate(value)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 2780, in validate
    (self.name, self.data_type.__name__, err))
BadValueError: Property file_blob must be convertible to a Blob instance (Blob() argument should be str instance, not EncodedPayload)

【问题讨论】:

  • “它返回一个错误”是非常无用的。什么错误?发布(完整的)堆栈跟踪!
  • 添加了跟踪。我认为很明显它从帖子中返回了一个值错误。感谢您的帮助。

标签: python google-app-engine email python-2.7 email-attachments


【解决方案1】:

检查此代码是否有帮助:

============================

   my_file = []
    my_list = []
    if hasattr(mail_message, 'attachments'):
        file_name = ""
        for filename, filecontents in mail_message.attachments:
            file_name = filename
            file_blob = filecontents.payload
            file_blob = file_blob.decode(filecontents.encoding)
            my_file.append(file_name)
            my_list.append(str(store_file(self, file_name, file_blob)))

【讨论】:

  • 你能解释一下你所做的与 OP 不同吗?
【解决方案2】:

附件负载是EncodedPayload 类的实例。附件有一个编码和一个可选的字符集。前者指的是base64等传输编码;后者适用于 UTF-8 等字符编码(这里的字符集有点过时且具有误导性)。 EncodedPayload.decode() 方法同时解码传输编码和文本编码,正如您所注意到的,如果您只想获取用户附加到其消息的原始字节,这并不是很有帮助。

您可以在此处采用多种方法,但我建议您复制 EncodedPayload 的用于解码传输编码的逻辑,如下所示:

if filecontents.encoding and filecontents.encoding.lower() != '7bit':
  try:
    payload = filecontents.payload.decode(filecontents.encoding)
  except LookupError:
    raise UnknownEncodingError('Unknown decoding %s.' % filecontents.encoding)
  except (Exception, Error), e:
    raise PayloadEncodingError('Could not decode payload: %s' % e)
else:
  payload = filecontents.payload

请注意,如果附件文本,则需要在存储时包含字符编码,否则在将其发送回用户时将无法解释 -原始文本可以使用任何字符编码进行编码。

同样,如果可以的话,您还应该保存附件的 mimetype,但这似乎不会在 API 的任何地方公开。您可能需要考虑完全避免使用 IncomingMessage 类,而是使用 Python 的 mime 消息模块解码 POST 请求的主体。

【讨论】:

  • @MarkFinch 如果filecontents 是字符串,则调用filecontents.decode() 会使用系统默认编解码器将其解码为Unicode 字符串。如果您的数据是 base64 编码的,您应该使用 base64.b64decode(filecontents)filecontents.decode('base64')。不过,邮件附件可以通过多种方式进行编码,并且它们的编码方式在附件的标头字段中指定。
  • 抱歉,我正在编辑评论,当您回复并删除它时,您的评论进入了,因为 5 分钟规则。如果我错了,请纠正我,但文件附件是以 Base64 编码的形式出现的。我们对它们运行 decode 以使它们恢复到原始状态。我认为预期的行为是,如果我可以使用这种方法存储 PDF、DOC、Jpeg 和 GIF 文件,那么它不应该对 TXT 文件有任何不同的处理。在这种情况下,重新编码它不起作用,因为它会产生一个错误,它期望一个 str 并得到 x。我正在寻找解决此案的方法。
  • Filecontents 是一个文本文档,它并不是真正的字符串,只是文件的不同格式。无论如何,AppEngine 将其视为字符串。使用 Try except 块可以解决消息重新传递问题(再次感谢),但文件会丢失,因为它会生成有关 Unicode 的错误。已经有人不得不解决这个问题。如果将文件存储在 blobstore 中可以解决此问题,那么我对这个答案很满意。无论如何,我打算在那里更改它,因为有人发送超过 1mb 的附件的可能性很小。无论如何,我想找到一个解决方法,因为这可能会在将来出现。
  • 正如我刚才所说,在字符串上调用 .decode() 不会对其进行 base64 解码(默认情况下;有许多可用的编解码器),它会将其解码为 un​​icode 字符串。如果你想要的是 base64 解码,那么你调用了错误的方法。
  • @MarkFinch BlobProperty 不能只“存储字节”,因为 unicode 字符串没有单一的规范编码。要让它存储一个 unicode 字符串,您首先必须自己选择一个编码并以这种方式对其进行编码。就猜测附件的模拟类型而言,如果不是因为传入邮件库的缺点,这不是您应该做的事情。我建议使用 Python 的 mime 消息 API 并自己解码消息(作为 POST 请求的主体发送),这样您就可以访问所有标头字段。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-05
  • 2018-08-02
  • 1970-01-01
  • 2021-03-06
  • 2012-03-24
  • 2015-11-08
相关资源
最近更新 更多