# 符号表上传说明

# 符号表上传

当前平台支持两种 符号表上传方式。

  • 手动上传
  • 接口上传


# 1、手动上传

  1. 按照文档说明将符号表文件压缩到一起:点击查看说明
  2. 访问应用,点击应用左侧菜单的 【管理工具 -> 版本管理】进入版本设置界面

image.png

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

image.png



# 2、接口上传

通过这种方式上传符号表的同时会自动创建解析版本。

该接口的请求方式是POST。


# 接口说明

https://yueying.effirst.com/rest/v1/api/symbol/upload?clientId=${clientId}×tamp=${timestamp}&signature=${signature}


# 请求时头部注意添加: 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:

image.png



# 上传代码

# python版本


# !/usr/bin/python

# -*- coding: utf-8 -*-


import hashlib

import os

import time


import requests



class SignBuilder(object):


   def __init__(self, client_id, secret, timestamp):

       self.__params = {}

       self.__client_id = client_id

       self.__secret = secret

       self.__timestamp = timestamp


   def add_params(self, key, value):

       self.__params[key] = value

       return self


   def body(self, value):

       self.__params["body"] = value

       return self


   def build(self):

       need_md5_txt = self.__client_id + self.__secret + self.__timestamp

       need_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"]:

               continue


           if self.__params[key] is None:

               body = body + key + "="


           else:

               body = body + key + '=' + str(self.__params[key])

       return body



class SymbolType(object):


   TEST = "test"


   RELEASE = "release"



class WpkApi(object):


   def __init__(self, host, client_id, secret):

       self.__host = host

       self.__client_id = client_id

       self.__secret = secret


   def 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_path


       data = {

           "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_suffix

           sign_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()

           return

       response.close()

       raise Exception("符号表上传失败,服务器响应:" + str(response.text.encode('utf-8')))




# 使用说明


wpk_api = WpkApi("https://yueying.effirst.com", "xxxx", "xxxxxxxx")

symbol_type = SymbolType.TEST

sub_ver = "release1d"


if "release" in sub_ver:

   symbol_type = SymbolType.RELEASE

wpk_api.upload_mapping("yueyingandroid", "1.2.0(1901191131053)", symbol_type, \

       "/Users/yueying/Documents/yueyingandroid_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 = 'yueyingandroid'

       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 + targetMapping

       String 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

       //upload

       def 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: {}' + mappingZip

               HttpResponseUtil.logResponse(it)

           }

           response.failure = {

               println 'unable to upload file: ' + mappingZip

               HttpResponseUtil.logResponse(it)

           }

       }

   }





# Java版本


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 params = new TreeMap<>();

   // json body

   private String body;

   private String clientId;

   private String secret;

   private Long timestamp;

   // 旧版1.0用的是随机串,并且不校验时常

   private String nonce;

   public SignBuilder addParams (Map 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 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;

       }

   }

}