从 Lambda 中的 S3 通知事件获取非 ASCII 文件名
Get non-ASCII filename from S3 notification event in Lambda
AWS S3 通知事件中表示文件名的 key
字段被 URL 转义。
当文件名包含空格或非 ASCII 字符时,这一点很明显。
例如,我将以下文件名上传到 S3:
my file řěąλλυ.txt
收到通知为:
{
"Records": [
"s3": {
"object": {
"key": u"my+file+%C5%99%C4%9B%C4%85%CE%BB%CE%BB%CF%85.txt"
}
}
]
}
我尝试使用以下方法解码:
key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key']).decode('utf-8')
但这会产生:
my file ÅÄÄλλÏ.txt
当然,当我尝试使用 Boto 从 S3 获取文件时,出现 404 错误。
tl;博士
您需要先将 URL 编码的 Unicode 字符串转换为字节 str,然后再对其进行 url 解析并解码为 UTF-8。
例如,对于具有文件名的 S3 对象:my file řěąλλυ.txt
:
>>> utf8_urlencoded_key = event['Records'][0]['s3']['object']['key'].encode('utf-8')
# encodes the Unicode string to utf-8 encoded [byte] string. The key shouldn't contain any non-ASCII at this point, but UTF-8 will be safer.
'my+file+%C5%99%C4%9B%C4%85%CE%BB%CE%BB%CF%85.txt'
>>> key_utf8 = urllib.unquote_plus(utf8_urlencoded_key)
# the previous url-escaped UTF-8 are now converted to UTF-8 bytes
# If you passed a Unicode object to unquote_plus, you'd have got a
# Unicode with UTF-8 encoded bytes!
'my file \xc5\x99\xc4\x9b\xc4\x85\xce\xbb\xce\xbb\xcf\x85.txt'
# Decodes key_utf-8 to a Unicode string
>>> key = key_utf8.decode('utf-8')
u'my file \u0159\u011b\u0105\u03bb\u03bb\u03c5.txt'
# Note the u prefix. The utf-8 bytes have been decoded to Unicode points.
>>> type(key)
<type 'unicode'>
>>> print(key)
my file řěąλλυ.txt
背景
AWS 犯了更改默认编码的大罪 - https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/
您应该从 decode()
得到的错误是:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 8-19: ordinal not in range(128)
key
的值是 Unicode。在 Python 2.x 中,您可以解码 Unicode,即使它没有意义。在 Python 2.x 解码 Unicode 中,Python 首先尝试将其编码为 [byte] str,然后再使用给定的编码对其进行解码。在Python 2.x中默认编码应该是ASCII,当然不能包含使用的字符。
如果您从 Python 获得了正确的 UnicodeEncodeError,您可能已经找到了合适的答案。在 Python 3 上,您根本无法调用 .decode()
。
以防万一其他人来这里希望获得 JavaScript 解决方案,这就是我最终得到的:
function decodeS3EventKey (key = '') {
return decodeURIComponent(key.replace(/\+/g, ' '))
}
经过有限的测试,它似乎工作正常:
test+image+%C3%BCtf+%E3%83%86%E3%82%B9%E3%83%88.jpg
解码为 test image ütf テスト.jpg
my+file+%C5%99%C4%9B%C4%85%CE%BB%CE%BB%CF%85.txt
解码为 my file řěąλλυ.txt
对于python 3:
from urllib.parse import unquote_plus
result = unquote_plus('input/%D0%BF%D1%83%D1%81%D1%82%D0%BE%D0%B8%CC%86.pdf')
print(result)
# will prints 'input/пустой.pdf'
AWS S3 通知事件中表示文件名的 key
字段被 URL 转义。
当文件名包含空格或非 ASCII 字符时,这一点很明显。
例如,我将以下文件名上传到 S3:
my file řěąλλυ.txt
收到通知为:
{
"Records": [
"s3": {
"object": {
"key": u"my+file+%C5%99%C4%9B%C4%85%CE%BB%CE%BB%CF%85.txt"
}
}
]
}
我尝试使用以下方法解码:
key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key']).decode('utf-8')
但这会产生:
my file ÅÄÄλλÏ.txt
当然,当我尝试使用 Boto 从 S3 获取文件时,出现 404 错误。
tl;博士
您需要先将 URL 编码的 Unicode 字符串转换为字节 str,然后再对其进行 url 解析并解码为 UTF-8。
例如,对于具有文件名的 S3 对象:my file řěąλλυ.txt
:
>>> utf8_urlencoded_key = event['Records'][0]['s3']['object']['key'].encode('utf-8')
# encodes the Unicode string to utf-8 encoded [byte] string. The key shouldn't contain any non-ASCII at this point, but UTF-8 will be safer.
'my+file+%C5%99%C4%9B%C4%85%CE%BB%CE%BB%CF%85.txt'
>>> key_utf8 = urllib.unquote_plus(utf8_urlencoded_key)
# the previous url-escaped UTF-8 are now converted to UTF-8 bytes
# If you passed a Unicode object to unquote_plus, you'd have got a
# Unicode with UTF-8 encoded bytes!
'my file \xc5\x99\xc4\x9b\xc4\x85\xce\xbb\xce\xbb\xcf\x85.txt'
# Decodes key_utf-8 to a Unicode string
>>> key = key_utf8.decode('utf-8')
u'my file \u0159\u011b\u0105\u03bb\u03bb\u03c5.txt'
# Note the u prefix. The utf-8 bytes have been decoded to Unicode points.
>>> type(key)
<type 'unicode'>
>>> print(key)
my file řěąλλυ.txt
背景
AWS 犯了更改默认编码的大罪 - https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/
您应该从 decode()
得到的错误是:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 8-19: ordinal not in range(128)
key
的值是 Unicode。在 Python 2.x 中,您可以解码 Unicode,即使它没有意义。在 Python 2.x 解码 Unicode 中,Python 首先尝试将其编码为 [byte] str,然后再使用给定的编码对其进行解码。在Python 2.x中默认编码应该是ASCII,当然不能包含使用的字符。
如果您从 Python 获得了正确的 UnicodeEncodeError,您可能已经找到了合适的答案。在 Python 3 上,您根本无法调用 .decode()
。
以防万一其他人来这里希望获得 JavaScript 解决方案,这就是我最终得到的:
function decodeS3EventKey (key = '') {
return decodeURIComponent(key.replace(/\+/g, ' '))
}
经过有限的测试,它似乎工作正常:
test+image+%C3%BCtf+%E3%83%86%E3%82%B9%E3%83%88.jpg
解码为test image ütf テスト.jpg
my+file+%C5%99%C4%9B%C4%85%CE%BB%CE%BB%CF%85.txt
解码为my file řěąλλυ.txt
对于python 3:
from urllib.parse import unquote_plus
result = unquote_plus('input/%D0%BF%D1%83%D1%81%D1%82%D0%BE%D0%B8%CC%86.pdf')
print(result)
# will prints 'input/пустой.pdf'