wechatpy / wechatpy

WeChat SDK for Python
https://www.wechatpy.org
MIT License
3.87k stars 807 forks source link

素材接口上传图片时,若名称含有中文则会报错 41005, message: media data missing hint #293

Open observerss opened 6 years ago

observerss commented 6 years ago

重现步骤 (Reproducing)

wx = WeChatClient(appid, appsecret)
wx.material.add('image', '中文图片.jpg')

问题实质

问题的实质是微信服务器端在解析multipart时不支持rfc2231格式的编码,而requests包发送的正是这种格式。

在文件名是中文时,比如"中文.jpg",

Workaround

我暂时的workaround是monkey patch编码部分

import six
import string
import requests

def encode_name(name, codec='utf-8'):
    """ 用类似curl的编码方式: \234... """
    vals = string.ascii_letters + string.digits + string.punctuation
    name = name.encode(codec)
    names = []
    for ch in name:
        if chr(ch) in vals:
            names.append(chr(ch))
        else:
            names.append('\\' + oct(ch)[2:])
    return ''.join(names)

def format_header_param(name, value):
    if not any(ch in value for ch in '"\\\r\n'):
        result = '%s="%s"' % (name, value)
        try:
            result.encode('ascii')
        except (UnicodeEncodeError, UnicodeDecodeError):
            pass
        else:
            return result
    if not six.PY3 and isinstance(value, six.text_type):  # Python 2:
        value = value.encode('utf-8')
    value = encode_name(value, 'utf-8')
    value = '%s="%s"' % (name, value)
    return value

requests.packages.urllib3.fields.format_header_param = format_header_param
messense commented 6 years ago

Thanks for reporting.

感觉作为库还是不 monkey patch 好,交给用户决定是否 patch/改用 ascii 文件名。 这个问题可以加入文档/FAQ 中。

observerss commented 6 years ago

之前的work around会让中文无法显示,实际上类似curl的编码方式应该是wireshark抓包时添加的,所以其实encode_name这个方法直接不进行encode就行了

def encode_name(name, codec='utf-8'):
    return name
observerss commented 6 years ago

@messense 对的我也这么觉得,感觉也没有像样一点的解决方案,只能作为FAQ了

messense commented 6 years ago

可以用 curl -v 来看 curl 传输的内容而不需要用 wireshark 抓包

observerss commented 6 years ago

curl -v 好像只能看到headers, multipart我没找到要怎么看

messense commented 6 years ago

Hmm, 可以试试这个:https://superuser.com/questions/291424/how-do-you-display-post-data-with-curl/1129975#1129975

messense commented 6 years ago

用 nc -l 8080 试了下:

curl -XPOST localhost:8080 -F meida=@测试.txt
nc -l 8080
POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 195
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------67398d7eafdff208

--------------------------67398d7eafdff208
Content-Disposition: form-data; name="meida"; filename="测试.txt"
Content-Type: text/plain

hello

--------------------------67398d7eafdff208--

https://superuser.com/a/1211368

NullYing commented 6 years ago

没有解决办法???

coderQuan commented 5 years ago

use urllib.quote

ret_data = client.material.add('image', (urllib.quote(os.path.basename(image_url)),
                                         image_file,
                                         mime_type))