快速开始
1. 前期准备
在开始调用 Open API 前,请先完成管理控制台配置见 调用Open API前的准备
并准备好以下信息(后续示例会用到):
repoId:三方应用 ID(与管理控制台配置一致)openApiSecret:Open API 密钥(用于计算认证头,需与控制台配置一致,三方自行定义)fileUrl:业务系统可下载源文件的地址(hostname 需与控制台配置一致)callback:任务完成后的回调地址(hostname 同样需一致)(可选)tokenType:中台访问fileUrl和回调callback时使用的鉴权类型,可选header或cookie(可选)tokenValue:与tokenType对应的鉴权值(tokenType为header时是Authorization值,cookie时格式如k1=v1;k2=v2)(可选)
相关基础概念可以查看:
2. 调用示例
调用链路为:
- 提交转换任务(
/convert) - 通过回调或主动查询拿到任务结果(
/queryTaskStatus) - 下载结果文件(
/download)
下文示例以“Word 转 PDF”为例(使用主动查询方式)。
接口文档:
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.math.BigInteger;
import java.util.UUID;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class OpenApiFullFlowDemo {
private static final String REPO_ID = "yourRepoId";
private static final String SECRET = "yourOpenApiSecret";
private static final String HOST = "http://{zofficehost}:8001";
private static final ObjectMapper MAPPER = new ObjectMapper();
public static void main(String[] args) throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault()) {
// Step 1: 提交转换任务(/convert),拿到 taskId
// callback不能为空,如果不实现回调,可以填一个假的url,但是host必须和管理控制台配置一致
// tokenType、tokenValue按需填写。文档中台调用下载(fileUrl)和回调通知(callback)时会带上tokenValue
// tokenType等于header代表tokenValue会在Authorization头中
// tokenType等于cookie代表tokenValue会在cookie中,注意tokenValue应该是`token=xxx`的形式
String convertBody = "{"
+ "\"fileUrl\":\"https://your-host/files/demo.docx\","
+ "\"filename\":\"demo.docx\","
+ "\"targetFilename\":\"demo.pdf\","
+ "\"callback\":\"https://your-host/openapi/callback\","
+ "\"tokenType\":\"header\","
+ "\"tokenValue\":\"Bearer xxx\""
+ "}";
String convertResp = new String(
execute(client, "POST", "/docs/publicapi/v1/convert", convertBody),
StandardCharsets.UTF_8
);
System.out.println("convert response: " + convertResp);
String taskId = readField(convertResp, "taskId");
if (taskId == null || taskId.length() == 0) {
throw new IllegalStateException("未从转换响应中解析到 taskId");
}
// Step 2: 轮询查询任务状态(/queryTaskStatus),直到成功并拿到 contentId
String contentId = null;
for (int i = 0; i < 60; i++) { // 最多轮询 60 次
String queryPath = "/docs/publicapi/v1/queryTaskStatus?taskId=" + taskId;
String queryResp = new String(
execute(client, "GET", queryPath, null),
StandardCharsets.UTF_8
);
System.out.println("query response: " + queryResp);
String code = readField(queryResp, "code");
if ("TaskSuccessNotify".equals(code) || "ConvertSuccessNotify".equals(code)) {
contentId = readField(queryResp, "detail.contentId");
break;
}
if ("TaskFailNotify".equals(code) || "ConvertFailNotify".equals(code)) {
throw new IllegalStateException("任务失败: " + queryResp);
}
Thread.sleep(1000L); // 每 1 秒查询一次
}
if (contentId == null || contentId.length() == 0) {
throw new IllegalStateException("任务未在预期时间内完成,未获取到 contentId");
}
// Step 3: 下载结果文件(/download)并保存到本地
String downloadPath = "/docs/publicapi/v1/download?taskId=" + taskId + "&contentId=" + contentId;
byte[] fileBytes = execute(client, "GET", downloadPath, null);
Files.write(Paths.get("result.pdf"), fileBytes);
System.out.println("download success: result.pdf");
}
}
// 通用请求执行方法:
// - body 非空时,自动按 JSON 请求体发送,并在签名 seed 末尾拼接 @@body
// - body 为空时,不发送请求体,签名 seed 不拼接 @@body
private static byte[] execute(CloseableHttpClient client, String method, String path, String body) throws Exception {
String timestamp = String.valueOf(System.currentTimeMillis());
String nonce = UUID.randomUUID().toString();
String authorization = buildAuthorization(timestamp, nonce, body);
RequestBuilder builder = RequestBuilder.create(method)
.setUri(HOST + path)
.setHeader("zOffice-auth-type", "s2s_MD5_sig")
.setHeader("zOffice-message-nonce", nonce)
.setHeader("timeStamp", timestamp)
.setHeader("Authorization", authorization);
if (body != null && !body.isEmpty()) {
builder.setHeader("Content-Type", "application/json");
builder.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON));
}
HttpUriRequest request = builder.build();
try (CloseableHttpResponse response = client.execute(request)) {
return EntityUtils.toByteArray(response.getEntity());
}
}
// 使用 Jackson 按点路径读取字段,例如:code、detail.contentId
private static String readField(String json, String path) throws Exception {
JsonNode root = MAPPER.readTree(json);
JsonNode current = root;
String[] segments = path.split("\\.");
for (String segment : segments) {
current = current.path(segment);
if (current.isMissingNode() || current.isNull()) {
return null;
}
}
return current.asText();
}
private static String buildAuthorization(String timestamp, String nonce, String body) throws Exception {
String seed = SECRET + "@@" + timestamp + "@@" + nonce;
if (body != null && !body.isEmpty()) {
seed += "@@" + body;
}
String sign = md5Hex(seed);
return REPO_ID + ":publicApi:" + sign;
}
private static String md5Hex(String input) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(input.getBytes(StandardCharsets.UTF_8));
return String.format("%032x", new BigInteger(1, md5.digest()));
}
}
完成以上步骤后,即可跑通 Open API 的基础调用链路;更多能力可继续查看 API 列表 中的内容处理、合并拆分、编辑保护等接口。