我想使用python脚本将zip文件上传到网站。该网站完全为此提供了一个API。但是,当我尝试使用它时,在加入所有要发送httplib连接请求的字符串时出现编码错误。我追查到的字符串是filedata
(我的zip文件)。Python HttpConnection请求编码错误
Traceback (most recent call last):
File "/Library/Application Junk/ProjectManager/Main.py", line 146, in OnUpload CurseUploader.upload_file('77353ba57bdeb5346d1b3830ed36171279763e35', 'wow', slug, version, VersionID, 'r', logText or '', 'creole', '', 'plain', zipPath)
File "/Library/Application Junk/ProjectManager/CurseUploader.py", line 83, in upload_file
content_type, body = encode_multipart_formdata(params, [('file', filepath)])
File "/Library/Application Junk/ProjectManager/CurseUploader.py", line 153, in encode_multipart_formdata
body = '\r\n'.join(L)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xcb in position 10: ordinal not in range(128)
编辑:按照要求,全部代码。
EDIT2:按照建议,尝试将所有非ascii字符串编码为ascii。它会抛出相同的错误,但现在在L[i] = value.encode("ascii")
。
from httplib import HTTPConnection
from os.path import basename, exists
from mimetools import choose_boundary
try:
import simplejson as json
except ImportError:
import json
def get_game_versions(game):
"""
Return the JSON response as given from /game-versions.json from curseforge.com of the given game
`game`
The shortened version of the game, e.g. "wow", "war", or "rom"
"""
conn = HTTPConnection('%(game)s.curseforge.com' % { 'game': game })
conn.request("GET", '/game-versions.json')
response = conn.getresponse()
assert response.status == 200, "%(status)d %(reason)s from /game-versions.json" % { 'status': response.status, 'reason': response.reason }
assert response.content_type == 'application/json'
data = json.loads(response.read())
return data
def upload_file(api_key, game, project_slug, name, game_version_ids, file_type, change_log, change_markup_type, known_caveats, caveats_markup_type, filepath):
"""
Upload a file to CurseForge.com on your project
`api_key`
The api-key from http://www.curseforge.com/home/api-key/
`game`
The shortened version of the game, e.g. "wow", "war", or "rom"
`project_slug`
The slug of your project, e.g. "my-project"
`name`
The name of the file you're uploading, this should be the version's name, do not include your project's name.
`game_version_ids`
A set of game version ids.
`file_type`
Specify 'a' for Alpha, 'b' for Beta, and 'r' for Release.
`change_log`
The change log of the file. Up to 50k characters is acceptable.
`change_markup_type`
Markup type for your change log. creole or plain is recommended.
`known_caveats`
The known caveats of the file. Up to 50k characters is acceptable.
`caveats_markup_type`
Markup type for your known caveats. creole or plain is recommended.
`filepath`
The path to the file to upload.
"""
assert len(api_key) == 40
assert 1 <= len(game_version_ids) <= 3
assert file_type in ('r', 'b', 'a')
assert exists(filepath)
params = []
params.append(('name', name))
for game_version_id in game_version_ids:
params.append(('game_version', game_version_id))
params.append(('file_type', file_type))
params.append(('change_log', change_log))
params.append(('change_markup_type', change_markup_type))
params.append(('known_caveats', known_caveats))
params.append(('caveats_markup_type', caveats_markup_type))
content_type, body = encode_multipart_formdata(params, [('file', filepath)])
print('Got here?')
headers = {
"User-Agent": "CurseForge Uploader Script/1.0",
"Content-type": content_type,
"X-API-Key": api_key}
conn = HTTPConnection('%(game)s.curseforge.com' % { 'game': game })
conn.request("POST", '/projects/%(slug)s/upload-file.json' % {'slug': project_slug}, body, headers)
response = conn.getresponse()
if response.status == 201:
print "Successfully uploaded %(name)s" % { 'name': name }
elif response.status == 422:
assert response.content_type == 'application/json'
errors = json.loads(response.read())
print "Form error with uploading %(name)s:" % { 'name': name }
for k, items in errors.iteritems():
for item in items:
print " %(k)s: %(item)s" % { 'k': k, 'name': name }
else:
print "Error with uploading %(name)s: %(status)d %(reason)s" % { 'name': name, 'status': response.status, 'reason': response.reason }
def is_ascii(s):
return all(ord(c) < 128 for c in s)
def encode_multipart_formdata(fields, files):
"""
Encode data in multipart/form-data format.
`fields`
A sequence of (name, value) elements for regular form fields.
`files`
A sequence of (name, filename) elements for data to be uploaded as files
Return (content_type, body) ready for httplib.HTTP instance
"""
boundary = choose_boundary()
L = []
for key, value in fields:
if value is None:
value = ''
elif value is False:
continue
L.append('--%(boundary)s' % {'boundary': boundary})
L.append('Content-Disposition: form-data; name="%(name)s"' % {'name': key})
L.append('')
L.append(value)
for key, filename in files:
f = file(filename, 'rb')
filedata = f.read()
f.close()
L.append('--%(boundary)s' % {'boundary': boundary})
L.append('Content-Disposition: form-data; name="%(name)s"; filename="%(filename)s"' % { 'name': key, 'filename': basename(filename) })
L.append('Content-Type: application/zip')
L.append('')
L.append(filedata)
L.append('--%(boundary)s--' % {'boundary': boundary})
L.append('')
for i in range(len(L)):
value = L[i]
if not is_ascii(value):
L[i] = value.encode("ascii")
body = '\r\n'.join(L)
content_type = 'multipart/form-data; boundary=%(boundary)s' % { 'boundary': boundary }
return content_type, body
我该如何解决它呢?
EDIT3:按照要求,印刷瓦尔的全部结果
fields: [('name', u'2.0.3'), ('game_version', u'1'), ('game_version', u'4'), ('game_version', u'9'), ('file_type', 'r'), ('change_log', u'====== 2.0.3\n* Jaliborc: Fixed a bug causing wrong items to be shown for leather, mail and plate slots\n* Jaliborc: Items are now organized by level as well\n\n====== 2.0.2\n* Jaliborc: Completly rewritten the categories dropdown to fix a bug\n\n====== 2.0.1\n* Jaliborc: Updated for patch 4.2\n* Jaliborc: Included all Firelands items\n\n===== 2.0.0\n* Jaliborc: Now works with 4.1\n* Jaliborc: Completely redesigned and improved\n* Jaliborc: Includes **all** items in-game right from the start\n* Jaliborc: Searches trough thousands of items in a blaze\n* Jaliborc: Mostly //Load on Demand//\n* Jaliborc: Only works on English clients. Versions for other clients should be released in a close future.\n\n====== 1.8.7\n* Added linkerator support for multiple chat frames\n\n====== 1.8.6\n* Fixed a bug when linking an item from the chat frame. \n\n====== 1.8.5\n* Added compatibility with WoW 3.3.5\n\n====== 1.8.3\n* Bumped TOC for 3.3\n\n====== 1.8.2\n* Bumped TOC for 3.2\n\n====== 1.8.1\n* TOC Bump + Potential WIM bugfix\n\n===== 1.8.0\n* Added "Heirloom" option to quality selector\n* Fixed a bug causing the DB to be reloaded on item scroll\n* Cleaned up the code a bit. Still need to work on the GUI/localization\n* Altered slash commands. See addon description for details.\n\n====== 1.7.2\n* Bumped the max item ID to check from 40k to 60k. Glyphs, etc, should now appear.\n\n====== 1.7.1\n* Fixed a crash issue when linking tradeskills\n\n===== 1.7.0\n* Made Wrath compatible\n* Seems to be causing a lot more CPU usage now, will investigate later.'), ('change_markup_type', 'creole'), ('known_caveats', ''), ('caveats_markup_type', 'plain')]
files: [('file', u'/Users/Jaliborc/Desktop/Ludwig 2.0.3.zip')]
这似乎包含一些unicode字符串。我应该将它们全部编码吗?
好吧,我加了完整的代码错误以前使用'.decode(“ISO-8859-1”)'的一个解释。并感谢您的快速回答! – Jaliborc
@Jibiborc:您没有显示(1)您获得的实际错误消息(2)错误(3)的完整回溯信息,您提到的API的URL。 “调用'\ r \ n'.join(L)时出现ascii错误不是一种解释,并且也不会解释 - 如果您可以解释错误,则不需要提出问题。只需使用复制/粘贴来显示错误消息和回溯。 –
好的,我已经在问题中包含了错误的完整追溯。我也尝试了你的建议,但也没有奏效。 API基本上由获取游戏版本的json请求(第一个函数)和用给定变量上传文件的请求组成(如'upload_file'中所述)。这并不重要:这是一个编码问题。 – Jaliborc