大疆智图api(java实现)
前言
最近在研究大疆智图api,感觉文档写的不是很详细,样例代码也看不懂,而且没有java样例代码。本文章只写了部分api调用,详细api查看API Reference (dji.com)本文章。这是本人第一次发表文章,也不是java大佬,如果文章和代码出现问题或者需要优化的地方,请大家纠错提醒。本文章尽可能把使用大疆智图api步骤写完整。
申请大疆智图api账号
访问大疆智图api网址DJI Developer
如果没有账号,则进行注册,有账号,则直接登入。
成为开发者
注册后,返回大疆智图api页面,点击右上角的控制台
进入控制台,申请成为开发者
填写信息,点击提交后,会刷新页面。如果没有开通,则点击免费使用
根据自己情况填写,应该很好过的。不出意外,两三天就可以申请成功。
申请成功后的控制台页面
我已经使用过了。App Key 和Secret Key 我们会用到。
准备建模素材
进入大疆智图api文档大疆智图API (dji.com)
点击快速入门,点击右侧 准备素材
根据需求下载素材,本文章选择3D素材。下载后进行解压。
实现步骤
主要代码
引入依赖,本文章使用的是hutool的请求工具类
<!-- hctool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.2</version>
</dependency>
用于各个请求处理(目前只写了POST,GET请求)
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Locale;
import static java.security.MessageDigest.getInstance;
public class TestDJ {
private static final String DJI_APP_KEY = "";//大疆智图API的APP Key
private static final String DJI_SECRET_KEY = "";//大疆智图API的Secret Key
private static final String HOST = "https://openapi-cn.dji.com";
/*
URL 大疆智图API 接口请求地址
payload 请求参数,如果不需要请求参数,请为空字符串
lowerMethod 请求方法,小写字母 get/post/put/delete
*/
public static JSONObject AccessDJ(String URI, String payload, String lowerMethod) {
try {
String url = HOST + URI;
String date = getFormattedDate();
String digest = getDigest(payload);
String requestSignature = calculateSignature(date, lowerMethod, URI, digest);
HttpRequest request = null;
if(lowerMethod.equals("post")){// delete和post请求还未测试 根据需求进行添加
request = HttpRequest.post(url);
}else if(lowerMethod.equals("get")){
request = HttpRequest.get(url);
}
request.header("Date", date)
.header("Digest", "SHA-256=" + digest)
.header("Authorization", "hmac username=\"" + DJI_APP_KEY + "\", algorithm=\"hmac-sha256\", headers=\"date @request-target digest\", signature=\"" + requestSignature + "\"")
.header("Content-Type", "application/json;charset=UTF-8");
if (!payload.isEmpty()) {
request.body(payload);
}
HttpResponse response = request.execute();
if (response.isOk()) {
System.out.println("response.body() = " + response.body());
return JSONUtil.parseObj(response.body());
} else {
System.out.println("HTTP error code: " + response.getStatus());
System.out.println("response = " + response);//查看失败原因
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static String getFormattedDate() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US)
.withZone(ZoneId.of("GMT"));
return formatter.format(ZonedDateTime.now(ZoneId.of("GMT")));
}
private static String getDigest(String data) throws NoSuchAlgorithmException {
return Base64.getEncoder().encodeToString(getInstance("SHA-256").digest(data.getBytes(StandardCharsets.UTF_8)));
}
private static String calculateSignature(String date, String method, String uri, String digest) throws NoSuchAlgorithmException, InvalidKeyException {
String content = "date: " + date + "\n@request-target: " + method + " " + uri + "\ndigest: SHA-256=" + digest;
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
hmacSha256.init(new SecretKeySpec(DJI_SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
return Base64.getEncoder().encodeToString(hmacSha256.doFinal(content.getBytes(StandardCharsets.UTF_8)));
}
}
根据文档获取各个API请求路径大疆智图API (dji.com)API Reference (dji.com)
获取Token
获取token的请求路径:
URI=/terra-rescon-be/v2/store/obtain_token
public static final String URI_TOKEN = "/terra-rescon-be/v2/store/obtain_token";
public static void main(String[] args) {
JSONObject accessToken = getAccessToken();
System.out.println(accessToken);
}
public static JSONObject getAccessToken() {
return AccessDJ(URI_TOKEN, "","post");
}
结果为JSON格式:
上传素材
利用阿里云的OSS
依赖
<!-- OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectResult;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestOSS {
/*
ENDPOINT 大疆智图API的外网访问网址 默认 https://oss-cn-hangzhou.aliyuncs.com
ACCESS_KEY_ID token的 accessKeyID
SECRET_ACCESS_KEY token的 secretAccessKey
BUCKET_NAME token的 cloudBucketName
STORE_PATH token的 sessionToken
TOKEN token的 sessionToken
FILE_PATH 本地素材的路径
*/
public static boolean uploadFile(String ENDPOINT, String ACCESS_KEY_ID, String SECRET_ACCESS_KEY, String BUCKET_NAME, String STORE_PATH, String TOKEN,String FILE_PATH) throws IOException {
boolean success = false;
OSS ossClient = new OSSClientBuilder().build(ENDPOINT,ACCESS_KEY_ID,SECRET_ACCESS_KEY,TOKEN);
List<Map<String, String>> uploadedFiles = new ArrayList<>();
File localFolder = new File(FILE_PATH);
Files.walk(localFolder.toPath())
.filter(Files::isRegularFile)
.forEach(path -> {
String relativePath = localFolder.toURI().relativize(path.toUri()).getPath();
String ossFilePath = STORE_PATH + relativePath.replace("\\", "/");
try {
PutObjectResult putResult = ossClient.putObject(BUCKET_NAME, ossFilePath, path.toFile());
String etag = putResult.getETag();
System.out.println("Uploaded: " + path + " -> " + ossFilePath + ", etag: " + etag);
Map<String, String> fileInfo = new HashMap<>();
fileInfo.put("name", relativePath.replace("\\", "/"));
fileInfo.put("etag", etag);
fileInfo.put("checksum", etag);
uploadedFiles.add(fileInfo);
} catch (Exception e) {
e.printStackTrace();
}
});
if (!uploadedFiles.isEmpty()) {
String json = JSONUtil.toJsonStr(uploadedFiles);
// 将JSON字符串写入文件
try {
Files.writeString(Paths.get("uploaded_files.json"), json);
System.out.println("文件写入成功!");
success = true;
} catch (Exception e) {
e.printStackTrace();
System.out.println("文件写入失败!");
}
}
ossClient.shutdown();
return success;
}
}
获取token和上传文件代码
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.io.IOException;
public class TestRequest {
public static final String URI_TOKEN = "/terra-rescon-be/v2/store/obtain_token";
private static final String ENDPOINT = "https://oss-cn-hangzhou.aliyuncs.com";
//我的本地素材地址
private static final String FILE_PATH = "D:\\DJ\\DJI Product\\Three-Dimension-3D\\Three-Dimension-3D\\3cm-3D-orthomosaic-70pic";
public static void main(String[] args) throws IOException {
boolean successUPload = isSuccessUPload();
System.out.println("successUPload = " + successUPload);
}
public static boolean isSuccessUPload() throws IOException {
//获取token
JSONObject accessToken = AccessDJ(URI_TOKEN, "", "post");
JSONObject dataToken = JSONUtil.parseObj(accessToken.get("data"));
String storePath = (String) dataToken.get("storePath");
//获取的storePath多了{fileName} 需要去掉
storePath=storePath.substring(0,storePath.length()-10);
//将本地素材上传到大疆oss
return uploadFile(ENDPOINT, (String) dataToken.get("accessKeyID"), (String) dataToken.get("secretAccessKey"), (String) dataToken.get("cloudBucketName"),
storePath, (String) dataToken.get("sessionToken"),FILE_PATH);
}
public static JSONObject getAccessToken() {
return AccessDJ(URI_TOKEN, "","post");
}
}
创建resoure
请求POST
URI=/terra-rescon-be/v2/resources
public static final String RESOURE_PATH = "/terra-rescon-be/v2/resources";
public static JSONObject creatRescoure(){
//payload 设置参数 参考API文档 name: resource名称 type: 类型
String payload="{\"name\": \"202409301736\", \"type\": \"map\"}";
return AccessDJ(RESOURE_PATH,payload ,"post");
}
public static void main(String[] args) throws IOException {
JSONObject jsonObject = creatRescoure();
System.out.println("jsonObject = " + jsonObject);
}
返回的JSON格式
关联文件
需要用到获取token的callbackParam,resource的uuid
一次上传最多50个文件
public static JSONObject associateFile(Object resource_uuid, Object callbackParam) throws IOException {
String filesJson = Files.readString(Paths.get("uploaded_files.json"));
JSONArray filesArray = JSONUtil.parseArray(filesJson);
List<JSONObject> payloadJson = new ArrayList<>();
int a=0;
String payload="";
for (int i = 0; i < filesArray.size(); i++) {
payloadJson.add((JSONObject) filesArray.get(i));
a++;
if(a>=50){
payload = JSONUtil.toJsonStr(Map.of("resourceUUID", resource_uuid, "callbackParam", callbackParam, "files", payloadJson));
AccessDJ(URI_ASSOCIATE_FILE,payload , "post");
a=0;
payloadJson.clear();
}
}
return AccessDJ(URI_ASSOCIATE_FILE,payload , "post");
}
创建Job
POST请求
URI=/terra-rescon-be/v2/jobs
public static final String CREAT_JOB_PATH = "/terra-rescon-be/v2/jobs";
public static JSONObject creatJob(){
//payload 设置参数 参考API文档 name: job名称
String payload="{\"name\": \"202409301736\"}";
return AccessDJ(CREAT_JOB_PATH,payload ,"post");
}
public static void main(String[] args) throws IOException {
JSONObject jsonObject = creatJob();
System.out.println("jsonObject = " + jsonObject);
}
返回JSON数据:
启动 3D 类型 job
URI=/terra-rescon-be/v2/jobs/{uuid from create job}/start
需要用到 rescoure的uuid,job的uuid
public static final String RESOURE_PATH = "/terra-rescon-be/v2/resources";
public static final String CREAT_JOB_PATH = "/terra-rescon-be/v2/jobs";
public static JSONObject creatJob(){
//payload 设置参数 参考API文档 name: job名称
String payload="{\"name\": \"202409301736\"}";
return AccessDJ(CREAT_JOB_PATH,payload ,"post");
}
public static JSONObject creatRescoure(){
//payload 设置参数 参考API文档 name: resource名称 type: 类型
String payload="{\"name\": \"202409301736\", \"type\": \"map\"}";
return AccessDJ(RESOURE_PATH,payload ,"post");
}
public static JSONObject start3DJob(){
JSONObject rescoureJson = creatRescoure();
Object resource_uuid = JSONUtil.parseObj(rescoureJson.get("data")).get("uuid");
JSONObject jobJson = creatJob();
System.out.println("启动3Djob");
Object job_uuid = JSONUtil.parseObj(jobJson.get("data")).get("uuid");
String url="/terra-rescon-be/v2/jobs/"+job_uuid+"/start";
JSONObject payload = new JSONObject();
payload.set("type",15).set("resourceUUID",resource_uuid).set("parameters",
"{\"parameter\":{\"output_mesh\": true,\"generate_obj\": true,\"generate_b3dm\": true,\"generate_osgb\": true}}");
return AccessDJ(url, payload.toString(), "post");
}
public static void main(String[] args) throws IOException {
JSONObject jsonObject = start3DJob();
System.out.println("jsonObject = " + jsonObject);
}
查询job状态
URI=/terra-rescon-be/v2/jobs/{uuid from create job}
需要job的uuid
public static JSONObject getJobStatus(){
JSONObject jobJson = creatJob();
JSONObject data = JSONUtil.parseObj(jobJson.get("data"));
Object jobUuid = data.get("uuid");
String url="/terra-rescon-be/v2/jobs/"+jobUuid;
return AccessDJ(url, "", "get");
}
public static void main(String[] args) throws IOException {
JSONObject jobStatus = getJobStatus();
System.out.println("jobStatus = " + jobStatus);
}
返回的JSON结果
主要数据uuid ,errCode,status
uuid job的uuid
status 建模进度
errCode 错误码
查看大疆智图重建错误指引.pdf (djicdn.com)
如果status为6,执行完成,说明建模完毕。接下来下载模型文件
下载模型文件
获取文件uuid列表
URI=/terra-rescon-be/v2/resources/{uuid form check job status}
建模完成后,查询job状态,返回的Json为
type为15 表示3D建模
status为6 表示成功
需要用到查询 job 状态返回的 outputResourceUuid
public static JSONObject getfilelist(){
String url="/terra-rescon-be/v2/resources/your outputResourceUuid";
return AccessDJ(url, "", "get");
}
public static void main(String[] args) throws IOException {
JSONObject getfilelist = getfilelist();
System.out.println("getfilelist = " + getfilelist);
}
返回的Json信息
fileUuids存放的是文件uuid
根据文件uuid下载文件
URI=/terra-rescon-be/v2/files/{file uuid}
从文件列表获取文件uuid,然后获取文件JOSN信息
public static void main(String[] args) throws IOException {
String uri="/terra-rescon-be/v2/files/uuid of your file";
JSONObject fileUuidJSON = DJIAuthRequest.AccessDJ(uri, "", "get");
}
返回的JSON信息
注意:获取文件的后缀可能不一样,不要自定义文件名称,使用需要使用到Json的name信息
完整代码
public void jobSuccessDownloadFile(){
//工作成功后下载文件
String downurl="/terra-rescon-be/v2/resources/{your outputResourceUuid}";//
JSONObject jsonObject = AccessDJ(downurl, "", "get");
//获取
List<String> fileUuids = (List<String>) JSONUtil.parseObj(jsonObject.get("data")).get("fileUuids");
for (String fileUuid :fileUuids
) {
String uri="/terra-rescon-be/v2/files/"+fileUuid;
JSONObject fileUuidJSON = AccessDJ(uri, "", "get");//获取文件信息Json
String url = (String) JSONUtil.parseObj(fileUuidJSON.get("data")).get("url");
// 使用HttpRequest获取文件
HttpResponse response = HttpRequest.get(url)
.timeout(5000) // 设置超时时间
.execute();
String savePath = "/file/"+JSONUtil.parseObj(fileUuidJSON.get("data")).get("name");//如果项目在D盘 则保存路径为D:/file/xxx.xxx
// 判断响应状态码是否为200
if (response.isOk()) {
// 读取响应体到字节数组
byte[] bytes = response.bodyBytes();
// 将字节数组写入文件
FileUtil.writeBytes(bytes, savePath);
System.out.println("文件下载成功,保存路径:" + savePath);
} else {
System.out.println("文件下载失败,状态码:" + response.getStatus());
}
}
}
流程完整代码
配好api请求路径
private static final String ENDPOINT = "https://oss-cn-hangzhou.aliyuncs.com";//外网访问网址
@Test
public void test() throws IOException {
//1. 获取token
System.out.println("获取token");
JSONObject accessToken = getAccessToken();
JSONObject dataToken = JSONUtil.parseObj(accessToken.get("data"));
Object callbackParam = dataToken.get("callbackParam");
String storePath = (String) dataToken.get("storePath");
storePath=storePath.substring(0,storePath.length()-10);
//将本地素材上传到大疆oss
boolean isSuccess = uploadFile(ENDPOINT, (String) dataToken.get("accessKeyID"), (String) dataToken.get("secretAccessKey"), (String) dataToken.get("cloudBucketName"),
storePath, (String) dataToken.get("sessionToken"));
if (isSuccess){
//2. 创建resoure
System.out.println("创建resoure");
String payloadResource="{\"name\": \"name of your 创建resoure\", \"type\": \"map\"}";
JSONObject resource = AccessDJ(URI_RESOURCE,payloadResource,METHOD_POST);
JSONObject dataResource = JSONUtil.parseObj(resource.get("data"));
Object uuid = dataResource.get("uuid");
System.out.println("uuid = " + uuid);
//3. 关联文件
System.out.println("关联文件");
associateFile(uuid,callbackParam);
//创建job
System.out.println("创建job");
String payloadJob="{\"name\":\"name of your job\"}";
JSONObject job = AccessDJ(URI_CREATE_JOB,payloadJob,METHOD_POST);
//启动3Djob
System.out.println("启动3Djob");
JSONObject data = JSONUtil.parseObj(job.get("data"));
Object job_uuid = data.get("uuid");
String url="/terra-rescon-be/v2/jobs/"+job_uuid+"/start";
JSONObject payload = new JSONObject();
payload.set("type",15).set("resourceUUID",uuid).set("parameters",
"{\"parameter\":{\"output_mesh\": true,\"generate_obj\": true,\"generate_b3dm\": true,\"generate_osgb\": true}}");
JSONObject jsonObject = AccessDJ(url, String.valueOf(payload), "post");
System.out.println("jsonObject = " + jsonObject);
}
//查看job工作完成状态
//工作成功后下载文件
}
如果job状态已经为完成就可以进行下载,只需要运行下载模型文件的完整代码即可,记得补充信息。
大疆制模导入模型
打开大疆制模软件(需要创建账号并激活)
点击新建项目
找到文件下载的目录,下载的文件如下:
AT文件夹没有xml文件可能是配置属性的问题,这个我还没有研究。
点击确定就可以加载模型了。
作者:养一个可爱的我