我踩了一个坑
春节前,尝试自己用Python写代码去获取自己BIMFACE中某个文件的所有元素,踩了一个坑,然后记录了下来,就在微博里分享出来,避免踩坑。
调用BIMFACE批量获取文件元素接口:https://api.bimface.com/data/v2/files/{fileId}/elements,当文件的元素较多时,会报如下错误:
{'code': 'system.error', 'message': 'number of elementId should be less than or equal to 1000'}
从错误可以看出,一次获取的元素太多了,如果把这个看作分页,那么分页的最大条目必须小于等于1000个,这时候问题就好解决了,不得不说,BIMFACE的提示还是很给力了。
踩坑过程
访问权限获取
通过BIMFACE给的APP_KEY和APP_SECRET,调用https://api.bimface.com/oauth2/token接口,我们可以获取到访问文件需要的ACCESS_TOKEN:
import requests
import base64
url = 'https://api.bimface.com/oauth2/token'
app_key = '[你的APP_KEY]'
app_secret = '[你的APP_SECRET]'
# Access Token有效期为7天,7天内该Token不会发生改变。
# BIMFACE 要求提供的appKey:appSecret字符串
key_and_secret = f'{app_key}:{app_secret}'
# BASE64 编码后字符串
encoded_kands = base64.b64encode(key_and_secret.encode()).decode()
# 设置 Authorization属性值
auth_str = f'Basic {encoded_kands}'
# 设置请求头
headers = {'Authorization': auth_str}
# 发送请求
response = requests.post(url, headers=headers)
# 输出返回的内容
print('响应内容(JSON格式):')
# 若要求返回字节类型,如:
# b'{"code":"success","message":null,"data":{"expireTime":"2024-02-13 16:04:51","token":"cn-cb19a350-aa23-46fa-8430-6e1fd70ed5ac"}}'
# 则使用response.content
json_result = response.json()
print(json_result)
# 获取AccessToken
print('AccessToken: ')
access_token = json_result['data']['token']
print(access_token)
如把该方法写成一个专门的授权类,那么:
import base64
import requests
ACCESS_TOKEN_API = 'https://api.bimface.com/oauth2/token'
# 访问凭证类
class Authorization:
def __init__(self, app_key, app_secret):
self.app_key = app_key
self.app_secret = app_secret
def get_access_token(self):
url = ACCESS_TOKEN_API
# BIMFACE 要求提供的appKey:appSecret字符串
key_and_secret = f'{self.app_key}:{self.app_secret}'
# BASE64 编码后字符串
encoded_kands = base64.b64encode(key_and_secret.encode()).decode()
# 设置 Authorization属性值
auth_str = f'Basic {encoded_kands}'
# 设置请求头
headers = {'Authorization': auth_str}
# 发送请求
response = requests.post(url, headers=headers)
# 获取JSON格式返回的内容
json_res = response.json()
# 返回AccessToken
return json_res['data']['token']
获取所有元素ID
调用接口https://api.bimface.com/data/v2/files/{fileId}/elements我们可以获得一个文件的所有元素,我们需要先调用接口https://api.bimface.com/data/v2/files/{fileId}/elementIds获得文件中的元素ID,然后根据这些元素ID去获得元素属性。
获取元素ID代码如下:
ELEMENT_IDS_API = 'https://api.bimface.com/data/v2/files/{fileId}/elementIds'
def get_element_ids(self, file_id, query_statement=''):
url = ELEMENT_IDS_API.format(fileId=file_id)
url += query_statement
jwt_auth = f'Bearer {self.access_token}'
# 请求头
headers = {'Authorization': jwt_auth}
# 发送请求
response = requests.get(url, headers=headers)
# 获取JSON格式返回内容
return response.json()
拿到所有元素ID后,就可以通过以下Python函数根据ID获取元素属性:
BATCH_GET_ELEMENTS_API = 'https://api.bimface.com/data/v2/files/{fileId}/elements'
def batch_get_elements(self, file_id, element_ids, query_statement=''):
url = BATCH_GET_ELEMENTS_API.format(fileId=file_id)
url += query_statement
jwt_auth = f'Bearer {self.access_token}'
# 请求header
headers = {'Authorization': jwt_auth}
# 构建body
body = {'elementIds': element_ids}
# 发送请求
response = requests.post(url, json=body, headers=headers)
# 获取JSON格式返回内容
return response.json()
获取所有元素属性
我一开始就想着全部获取:
def try_fetch_all_elements(access_token, file_id, element_ids):
data_service = DataService(access_token)
json = data_service.batch_get_elements(file_id, element_ids)
print(json)
因我的文件有1387个元素,问题就暴露了:
{'code': 'system.error', 'message': 'number of elementId should be less than or equal to 1000'}
于是,按照错误提示,我们修改了调用方法:
def fetch_all_elements(access_token, file_id, element_ids, step=1000):
length = len(element_ids)
elements = []
curr_idx = 0
data_service = DataService(access_token)
while curr_idx < length:
upper = curr_idx + step
if upper > length:
upper = length
json = data_service.batch_get_elements(file_id, element_ids[curr_idx:upper])
if json['code'] == 'success':
elements += json['data']
curr_idx += step
return elements
将每次获取的最大值限制在1000个及以下,问题可以解决。
最后的两个数据访问代码如下:
import base64
import requests
BATCH_GET_ELEMENTS_API = 'https://api.bimface.com/data/v2/files/{fileId}/elements'
ELEMENT_IDS_API = 'https://api.bimface.com/data/v2/files/{fileId}/elementIds'
# 数据服务类
class DataService:
def __init__(self, access_token):
self.access_token = access_token
# 每次请求小于等于1000个元素ID
def batch_get_elements(self, file_id, element_ids, query_statement=''):
url = BATCH_GET_ELEMENTS_API.format(fileId=file_id)
url += query_statement
jwt_auth = f'Bearer {self.access_token}'
# 请求header
headers = {'Authorization': jwt_auth}
# 构建body
body = {'elementIds': element_ids}
# 发送请求
response = requests.post(url, json=body, headers=headers)
# 获取JSON格式返回内容
return response.json()
def get_element_ids(self, file_id, query_statement=''):
url = ELEMENT_IDS_API.format(fileId=file_id)
url += query_statement
jwt_auth = f'Bearer {self.access_token}'
# 请求头
headers = {'Authorization': jwt_auth}
# 发送请求
response = requests.get(url, headers=headers)
# 获取JSON格式返回内容
return response.json()
调用代码如下:
import sys, os
import time
import json
sys.path.append(os.path.abspath('.'))
from common.util import DataService
def main():
access_token = '[你的Access_Token]'
file_id = '[你的FILE_ID]'
data_service = DataService(access_token)
print('all element ids: ')
json_ids = data_service.get_element_ids(file_id)
print(json_ids)
print(f'there are {len(json_ids["data"])} elements.')
# 纳秒为单位的计时器
start_time = time.perf_counter_ns()
# elements = fetch_all_elements(access_token, file_id, json_ids['data'])
elements = fetch_all_elements(access_token, file_id, json_ids['data'])
if not elements:
return
finish_time = time.perf_counter_ns()
time_span = finish_time - start_time
# 写入到文件
file_name = os.path.abspath(f'./data/{file_id}_{time.strftime("%Y-%m-%d_%H%M%S", time.localtime())}.json')
with open(file_name, 'wt') as fout:
fout.write(json.dumps(elements))
fout.flush()
print('number of elements: ')
print(len(elements))
print(f'it costs {time_span / 1000.0}μs to get all elements.')
def fetch_all_elements(access_token, file_id, element_ids, step=1000):
length = len(element_ids)
elements = []
curr_idx = 0
data_service = DataService(access_token)
while curr_idx < length:
upper = curr_idx + step
if upper > length:
upper = length
json = data_service.batch_get_elements(file_id, element_ids[curr_idx:upper])
if json['code'] == 'success':
elements += json['data']
curr_idx += step
return elements
if __name__ == '__main__':
main()
不自做轮子
这次踩坑我自己用Python来调用,实际上,BIMFACE的SDK比较成熟,使用好的SDK,能提升我们的开发效率。
[bimface-java-sdk](https://github.com/bimface/bimface-java-sdk)
[BIMFace.SDK](https://gitee.com/NAlps/BIMFace.SDK)