python requests 在非 ASCII 文件名上的问题

5 投票
1 回答
3032 浏览
提问于 2025-04-18 10:57

我正在使用Python的requests库发送请求。当附件的参数中有一些非ASCII字符时,会出现异常;而当只有ASCII字符时,一切正常。

你可以在这里看到异常信息

response = requests.post(url="https://api.mailgun.net/v2/%s/messages" % utils.config.mailDomain,
                auth=("api", utils.config.mailApiKey),
                data={
                        "from" : me,
                        "to" : recepients,
                        "subject" : subject,
                        "html" if html else "text" : message
                    },

                files= [('attachment', codecs.open(f.decode('utf8'))) for f in attachments] if attachments and len(attachments) else []                                
                )

编辑:在用UTF-8解码文件名后,我没有遇到异常,但文件没有被附加上。我调试了requests,使用一个文件名中只有ASCII字符的文件,构建的请求头是:

{'Content-Type': None, 'Content-Location': None, 'Content-Disposition': u'form-data; name="attachment"; filename="Hello.docx"'}

这个请求成功了,我收到了带附件的邮件。

但是,当我使用一个包含希伯来字符的文件时,请求的头是:

{'Content-Type': None, 'Content-Location': None, 'Content-Disposition': 'form-data; name="attachment"; filename*=utf-8\'\'%D7%91%D7%93%D7%99%D7%A7%D7%94.doc'}

我收到了邮件,但没有附件。有什么想法吗?

1 个回答

3

当文件名包含非ASCII字符时,requests库会按照标准RFC 2231进行编码。这个格式就是你看到的:filename*=utf-8''......。看起来MailGun不支持这个标准,所以非ASCII文件名就丢失了。你可以联系MailGun确认他们对Unicode文件名的期望格式。

作为一个不太完美的解决办法,你可以将非ASCII字符替换成:

def replace_non_ascii(x): return ''.join(i if ord(i) < 128 else '_' for i in x) 

并在调用requests时明确指定文件名(假设attachments是一个包含Unicode文件名的列表):

files= [('attachment', (replace_non_ascii(f), codecs.open(f))) for f in attachments] ...

编辑

如果你想自定义头部格式,假设(而不是标准的RFC 2231)MailGun可以接受这种格式:

filename="%D7%91%D7%93%D7%99%D7%A7%D7%94.doc"

那么你可以将文件名自定义为:

import urllib
def custom_filename(x): return urllib.quote(x.encode('utf8'))

files= [('attachment', (custom_filename(f), codecs.open(f))) for f in attachments] ...

根据MailGun的反馈,可能需要调整requests的代码,或者使用更底层的库(如urllib2)。希望他们能支持RFC 2231。

撰写回答