上篇我们看到了AutoCAD /O的功能和基本原理,在申请到API key以后,就可以进行开发了。接触一个新技术,都会以一个精巧的例子起头,俗称 Hello World。但AutoCAD I/O Hello World并不是简单写个模板,弹出个对话框就ok了,不过也不用紧张,一起来看看。
AutoCAD I/O是web service,程序只要能发送HTTP请求即可。我们将这些HTTP请求的常用流程封装了一个.NET service reference。因此让我们以一个.NET程序开始I/O的旅行:
添加 Newtonsoft.Json 引用,这是用来解析AutoCAD I/O登陆后的Json字,获取到口令(token)。
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace MyTestAutoCADIO
{
class Program
{
static void Main(string[] args)
{
//创建与AutoCAD IO 通讯的代理人口
Uri uri = new Uri("https://developer.api.autodesk.com/autocad.io/v1/");
//服务的容器
var container = new AcadIO.Container(uri);
//指定使用Json进行通讯
container.Format.UseJson();
//开发者的 API key
var clientId = "你自己的API key";
//开发者的 API secret
var clientSecret = "你自己的API seret";
//AutoCAD IO 返回的口令
string token = null;
using (var client = new HttpClient())
{
//配置HTTP请求。其中有两个参数是开发者的API key和 secret
var values = new List>();
values.Add(new KeyValuePair("client_id", clientId));
values.Add(new KeyValuePair("client_secret", clientSecret));
values.Add(new KeyValuePair("grant_type", "client_credentials"));
var requestContent = new FormUrlEncodedContent(values);
//向AutoCADI/O发出认证请求
//服务端点地址为: https://developer.api.autodesk.com/authentication/v1/authenticate
var response = client.PostAsync("https://developer.api.autodesk.com/authentication/v1/authenticate",
requestContent).Result;
//得到响应,查看返回结果
var responseContent = response.Content.ReadAsStringAsync().Result;
//解析返回的Json字串
var resValues = JsonConvert.DeserializeObject>(responseContent);
//提取出口令(token)
token = resValues["token_type"] + " " + resValues["access_token"];
}
//将口令设置到随后所有AutoCAD I/O相关请求的HTTP头
if (!string.IsNullOrEmpty(token))
container.SendingRequest2 += (sender, e) => e.RequestMessage.SetHeader("Authorization", token);
//接下来可以进行AutoCAD I/O的其它操作
//.....
}
}
}
Activity是规定具体对图纸做什么样的操作。AutoCAD I/O有Activity集合,类似一个篮子,存放Activity。一般是你自己创建的,也包含别人分享的Activity。对应的集合是AcadIO.Container.Activities。基于上面的代码框架,增加一个函数,并在Main函数中调用它。
//接下来可以进行AutoCAD I/O的其它操作
//.....
//遍历Activity
GetActivities(container);
}//Main函数结尾
static void GetActivities(AcadIO.Container container)
{
foreach (var act in container.Activities)
{
Console.WriteLine("{0} {1}", act.UserId, act.Id);
Console.WriteLine(" Input Parameters:");
foreach (var inputP in act.Parameters.InputParameters)
{
Console.WriteLine(" {0}", inputP.Name);
}
Console.WriteLine(" Output Parameters:");
foreach (var outputP in act.Parameters.OutputParameters)
{
Console.WriteLine(" {0}", outputP.Name);
}
}
每个Activity也有标识Id,UserId是AutoCAD I/O自动分配的Guid,而Id是在创建Activity时开发者给的名字。我这里的Activity如下,其中有一个分享型的是批处理DWG为PDF (PlotToPDF)。每个Activity都规定了输入参数和输出参数。例如PlotToPDF,输入参数名为“HostDwg”,输出参数名为"Result"。
注: 由于.NET封装了服务,所以从表面看我们直接调用了相关内容,但实际背后是发送的HTTP请求。
有了Activity,就可以构建批处理任务,也就是前篇提到的的WorkItem。规定DWG图纸在哪里,产生的结果放哪里,用哪个Activity进行操作。例如,如果我们要进行DWG转PDF的批处理,可以这样建立WorkItem。仍旧基于上面的代码,创建一个新函数。几点说明:
1. WorkItem不需要设置Id,AutoCAD I/O会自动分配
2. 输入参数的来源(也就是需要处理的DWG)可以是A360云存储里的,也可以是其它云存储。Resource规定之。访问云存储的文件,对应的云服务的授权,一般是一种签名后的URL。StorageProvider = "A360"表明是A360的云存储,而StorageProvider = "Generic"表明是其它云存储。例如本例是我的亚马逊(AWS)上的一个文件。
3. 按照Activity的参数规定,提供文件。传出的结果,如果Resource= null,则将结果文件放到AutoCAD I/O存储中。
//接下来可以进行AutoCAD I/O的其它操作
//.....
//遍历Activity
GetActivities(container);
//执行批处理任务 ( WorkItem)
CreateWorkItem(container);
}//Main函数结尾
static void CreateWorkItem(AcadIO.Container container)
{
//新建WorkItem
var wi = new AcadIO.WorkItem()
{
UserId = "", //必须为空,AutoCAD I/O会自动分配Guid
Id = "", //必须为空,AutoCAD I/O会自动分配Guid
Arguments = new AcadIO.Arguments(),
Version = 1,
//用哪个Activity
ActivityId = new AcadIO.EntityId() { UserId = "Shared", Id = "PlotToPDF" }
};
//设置输入参数
wi.Arguments.InputArguments.Add(new AcadIO.Argument()
{
//对应Activity哪个参数
Name = "HostDwg",
//来源。本例是AWS上的文件
Resource = "https://xiaodongtemptestio.s3-ap-southeast-1.amazonaws.com/Lights.dwg?AWSAccessKeyId=XXXXXXXExpires=1429936154&Signature=SKgpVs2ebxRKngiv8AiWMHCkFLM%3D",
//非A360数据源
StorageProvider = "Generic"
});
//设置输出参数
wi.Arguments.OutputArguments.Add(new AcadIO.Argument()
{
//对应Activity哪个参数
Name = "Result",
//非A360数据源
StorageProvider = "Generic",
//HTTP动作 - 将结果放到对应的云存储
HttpVerb = "POST",
//如果设置为null,则缺省放到AutoCAD I/O存储空间里
Resource = null
});
//添加此WorkItem, 也就是开始启动任务
container.AddToWorkItems(wi);
container.SaveChanges();
//看看AutoCAD I/O为WorkItem分配了什么Guid
Console.WriteLine("UserId = {0}, Id= {1}", wi.UserId, wi.Id);
container.MergeOption = System.Data.Services.Client.MergeOption.OverwriteChanges;
//等待,看看该任务WorkItem是否执行完毕
do
{
System.Threading.Thread.Sleep(10000);
wi = container.WorkItems.Where(p => p.UserId == wi.UserId && p.Id == wi.Id).SingleOrDefault();
}
while (wi.Status == "Pending" || wi.Status == "InProgress");
if (wi.Status == "Succeeded")
{
//若成功结束,看看转换后的文件url
Console.WriteLine("The result is downloadable at {0}", wi.Arguments.OutputArguments.First().Resource);
}
}
前面的文件流,可以下载到本地机器。写个小函数。并在CreateWorkItem函数中调用。
//等待,看看该任务WorkItem是否执行完毕
do
{
System.Threading.Thread.Sleep(10000);
wi = container.WorkItems.Where(p => p.UserId == wi.UserId && p.Id == wi.Id).SingleOrDefault();
}
while (wi.Status == "Pending" || wi.Status == "InProgress");
if (wi.Status == "Succeeded")
{
//若成功结束,看看转换后的文件url
Console.WriteLine("The result is downloadable at {0}", wi.Arguments.OutputArguments.First().Resource);
//下载结果文件
String localFilePath = String.Empty;
if (!Download(wi.Arguments.OutputArguments.First().Resource.ToString(), ref localFilePath))
{
Console.WriteLine("failed to get the result file!");
}
}
}//CreateWorkItem函数结尾
public static bool Download(String url, ref String localFilePath)
{
try
{
if (String.IsNullOrEmpty(url))
return false;
// Load the url in web browser
if (url.StartsWith("http://") || url.StartsWith("https://"))
{
//下载到我的文档文件夹
String filename = Path.GetFileName(new Uri(url).AbsolutePath);
localFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), filename);
using (System.Net.WebClient wc = new System.Net.WebClient())
{
wc.DownloadFile(url, localFilePath);
}
if (!String.IsNullOrEmpty(localFilePath))
{
return true;
}
}
}
catch (System.UriFormatException)
{
Console.WriteLine(url + "could not be loaded.");
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
localFilePath = String.Empty;
return false;
}