符号表上传
当前平台支持两种 符号表上传方式 :
- 手动上传
- 接口上传
手动上传
- 按照文档说明将符号表文件压缩到一起:点击查看说明
登录平台,点击平台左下角的 设置 进入应用设置界面

点击 版本管理 ,找到对应的版本,点击 上传 ,将第一步压缩好的符号表文件上传即可

接口上传
通过这种方式上传符号表的同时会自动创建解析版本
该接口的请求方式是POST
**
接口说明
请求时头部注意添加: content-type: multipart/form-data;
request字段说明:
| 字段名 | 说明 | 示例 | 是否必填 |
|---|---|---|---|
| clientId | 调用方的clientId(clientId需要联系啄木鸟申请) | 是 | |
| app | 应用标识 | UCBrowser | 是 |
| version | 主版本号(若版本不存在,会为其创建该版本),如果有流水号,需要用于反混淆/符号化,则需要添加到括号中 | 默认版本号:12.1.0.12 如果带有流水号: 12.1.0.12(190219173829) |
是 |
| versionSuffix | 版本后缀,一般为子版本号 | appc\beta\trial | 否 |
| symbolType | 类型,测试版传test、正式版传release | test、release | 是 |
| file | 符号文件,使用zip压缩,压缩格式参照:点击查看说明 | 是 |
response:
{"id": "8cbdbe8a-173c-466c-82a2-41be0c2ef0f9","code": "200","message": "OK","data": 123, // 符号表的唯一id"error": null}
上传代码:
python版本
# !/usr/bin/python# -*- coding: utf-8 -*-import hashlibimport osimport timeimport requestsclass SignBuilder(object):def __init__(self, client_id, secret, timestamp):self.__params = {}self.__client_id = client_idself.__secret = secretself.__timestamp = timestampdef add_params(self, key, value):self.__params[key] = valuereturn selfdef body(self, value):self.__params["body"] = valuereturn selfdef build(self):need_md5_txt = self.__client_id + self.__secret + self.__timestampneed_md5_txt = need_md5_txt + self.__build_params()md5_encoder = hashlib.md5()md5_encoder.update(need_md5_txt)signature = md5_encoder.hexdigest()return signature.lower()def __build_params(self):keys = self.__params.keys()keys = sorted(keys)body = ""# 组件请求参数体for key in keys:if key == "" or key in ["clientId", "timestamp", "signature"]:continueif self.__params[key] is None:body = body + key + "="else:body = body + key + '=' + str(self.__params[key])return bodyclass SymbolType(object):TEST = "test"RELEASE = "release"class WpkApi(object):def __init__(self, host, client_id, secret):self.__host = hostself.__client_id = client_idself.__secret = secretdef upload_mapping(self, app, ver, symbol_type, file_path, ver_suffix=None):if file_path is None or not os.path.exists(file_path):raise Exception("Mapping文件不存在:" + str(file_path))api_path = "/rest/v1/api/symbol/upload"req_url = self.__host + api_pathdata = {"app": app,"version": ver,"symbolType": symbol_type}files = {"file": open(file_path, 'rb'),}timestamp = str(int(time.time() * 1000))sign_builder = SignBuilder(self.__client_id, self.__secret, timestamp)\.add_params("app", app) \.add_params("version", ver) \.add_params("symbolType", symbol_type)# 子版本号,非必选if ver_suffix is not None:data["versionSuffix"] = ver_suffixsign_builder.add_params("versionSuffix", ver_suffix)sign_str = sign_builder.build()req_url = req_url + "?clientId=%s×tamp=%s&signature=%s" % (self.__client_id, timestamp, sign_str)response = requests.post(req_url, data=data, files=files)response_json = response.json()if response.status_code == requests.codes.ok and str(response_json["code"]) == "200":print response.text.encode('utf-8')response.close()returnresponse.close()raise Exception("符号表上传失败,服务器响应:" + str(response.text.encode('utf-8')))
使用说明:
wpk_api = WpkApi("https://wpk.uc.cn", "xxxx", "xxxxxxxx")symbol_type = SymbolType.TESTsub_ver = "release1d"if "release" in sub_ver:symbol_type = SymbolType.RELEASEwpk_api.upload_mapping("youyuandroid", "1.2.0(1901191131053)", symbol_type, \"/Users/kevin/Documents/youyuandroid_1.0.0.2_190119113105_dev_maps.zip", \ver_suffix=sub_ver)
groovy版本
private void uploadMapping(String mappingZip) {if (mappingZip == null) {return}File mappingZipFile = new File(mappingZip)assert mappingZipFile.exists()String version = getBuildInfo().getVersion() + '(' + getBuildInfo().getBuildSequence() + ')'String subVersion = getBuildInfo().getSubversion()String app = 'novelapp'String symbolType = 'release'.equals(subVersion) ? 'release' : 'test'long timeStamp = System.currentTimeMillis()SignBuilder signBuilder = new SignBuilder().addClientId(UcAndroidPluginConstant.WPK_CLIENT_ID).addTimestamp(timeStamp).addSecret(UcAndroidPluginConstant.WPK_CLIENT_SECRET).addParam('versionSuffix', subVersion).addParam('app', app).addParam('version', version).addParam('symbolType', symbolType)String signStr = signBuilder.buildSign()String targetMapping = UcAndroidPluginConstant.CRASH_SDK_APP_ID + "_" + getBuildInfo().getVersion() + "_" + getBuildInfo().getBuildSequence() + "_" + getBuildInfo().getSubversion() + "_maps.zip"logger.info('uploaded source file: {} , target name', mappingZip, targetMapping)println 'uploaded source file: {} , target name' + mappingZip + targetMappingString url = UcAndroidPluginConstant.CRASH_SDK_MAPPING_UPLOAD_URL + String.format('?clientId=%s×tamp=%s&signature=%s&app=%s&version=%s&versionSuffix=%s&symbolType=%s', UcAndroidPluginConstant.WPK_CLIENT_ID, String.valueOf(timeStamp), signStr, app, version, subVersion, symbolType)println 'url: ' + url//uploaddef http = new HTTPBuilder(url)http.ignoreSSLIssues()http.request(Method.POST) { request ->def entityBuilder = MultipartEntityBuilder.create().addBinaryBody('file', mappingZipFile)request.entity = entityBuilder.build()response.success = {println 'uploaded file: {}' + mappingZipHttpResponseUtil.logResponse(it)}response.failure = {println 'unable to upload file: ' + mappingZipHttpResponseUtil.logResponse(it)}}}
package com.xxx.build.gradle.helper;import java.util.Map;import java.util.Map.Entry;import java.util.TreeMap;import org.apache.commons.codec.digest.DigestUtils;public class SignBuilder {private Map<String, String> params = new TreeMap<>();// json bodyprivate String body;private String clientId;private String secret;private Long timestamp;// 旧版1.0用的是随机串,并且不校验时常private String nonce;public SignBuilder addParams (Map<String, String> params) {this.params.clear();this.params.putAll(params);return this;}public SignBuilder addParam(String name, String value) {params.put(name, value);return this;}public SignBuilder addBody (String body) {this.addParam("body", body);return this;}public SignBuilder addClientId(String clientId) {this.clientId = clientId;return this;}public SignBuilder addSecret(String secret) {this.secret = secret;return this;}public SignBuilder addTimestamp(Long timestamp) {this.timestamp = timestamp;return this;}public SignBuilder nonce(String nonce) {this.nonce = nonce;return this;}/*** 构建签名信息* @return*/public String buildSign() {StringBuilder singBuilder = new StringBuilder(clientId).append(secret).append(timestamp);singBuilder.append(this.buildParams());return DigestUtils.md5Hex(singBuilder.toString());}/*** 拼接请求参数* @return*/private StringBuilder buildParams () {StringBuilder rst = new StringBuilder();for (Entry<String, String> entry : params.entrySet()) {if (isBlank(entry.getKey())) {continue;}if (null == entry.getValue()) {rst.append(entry.getKey()).append("=");} else {String value = entry.getValue();rst.append(entry.getKey()).append("=").append(value);}}return rst;}public static boolean isBlank(String str) {int strLen;if (str != null && (strLen = str.length()) != 0) {for(int i = 0; i < strLen; ++i) {if (!Character.isWhitespace(str.charAt(i))) {return false;}}return true;} else {return true;}}}
