Working with Attachments and Images
This topic explains how to work with attachments and images in Agility.
Overview
Working with attachments and images via the .NET SDK is slightly different from other types of assets in that it involves working with specialized endpoints. These specialized endpoints are used for uploading and downloading the attachment and image data, which in most cases is in a binary format.
Adding attachment and images to a Digital.ai Agility asset is a two step process:
- Create the attachment or image asset as you would any other asset.
- Use the specialized attachment.img or embedded .img endpoints to upload the file data.
When uploading or downloading the attachment or image file data, you can make use of the System.IO.Stream class in the .NET Framework to work with the file data.
Prior to working with an attachment or image asset in VersionOne, you must first instantiate a V1Connector and Services object:
V1Connector connector = V1Connector
.WithInstanceUrl("<Server Base URI>")
.WithUserAgentHeader("AppName", "1.0")
.WithAccessToken("1.rWM8lKLk+PnyFxkEWVX5Kl2u6Jk=")
.Build();
IServices services = new Services(connector);
In versions of the SDK prior to the 15.0.0.0 release, you would also have to instantiate a connector for theMetaModelobject. However, starting with the 15.0.0.0 release, that is no longer necessary. TheMetaModelobject is now available from theMetaproperty of theServicesobject.
Creating an Attachment
Prior to the 15.1.0.0 release of the SDK, creating an attachment for an asset involved first creating an Attachment asset then using the Attachments object to upload the attachment data. Starting with the 15.1.0.0 release, a much simpler SaveAttachmentmethod is available on the Services object. With the SaveAttachment method, you no longer have to create a special V1Connector to the attachment.img endpoint to upload attachments to Agility.
Creating an Attachment Using the Services Object
To create an attachment and associate it with an asset in Agility, call the SaveAttachment method of the Services object passing in the path and name of the file, the asset to associate with, and the name to use for the attachment. This example first creates a new story then adds the attachment to it:
//Create a story in Scope:0.
var storyType = services.Meta.GetAssetType("Story");
var newStory = services.New(storyType, services.GetOid("Scope:0"));
var nameAttribute = storyType.GetAttributeDefinition("Name");
var name = string.Format("Story with an attachment");
newStory.SetAttributeValue(nameAttribute, name);
services.Save(newStory);
//Create an attachment for the story.
string fileName = @"C:\Temp\versioneone.jpg";
Oid attachmentOID = services.SaveAttachment(fileName, newStory, "Attachment for " + newStory.Oid.Momentless);
Creating an Attachment Using the Attachment Object
To create an attachment and associate it with an asset in Agility, you must first create an Attachment asset and retain its OID value which will be used to upload the attachment. This example creates an attachment asset for the Story with ID 6052.
Oid storyOID = services.GetOid("Story:6052");
string fileName = "versionone.png";
string mimeType = MimeType.Resolve(fileName);
IAssetType attachmentType = services.Meta.GetAssetType("Attachment");
IAttributeDefinition attachmentAssetRelation = attachmentType.GetAttributeDefinition("Asset");
IAttributeDefinition attachmentContent = attachmentType.GetAttributeDefinition("Content");
IAttributeDefinition attachmentContentType = attachmentType.GetAttributeDefinition("ContentType");
IAttributeDefinition attachmentFileName = attachmentType.GetAttributeDefinition("Filename");
IAttributeDefinition attachmentName = attachmentType.GetAttributeDefinition("Name");
Asset attachment = services.New(attachmentType, Oid.Null);
attachment.SetAttributeValue(attachmentAssetRelation, storyOID);
attachment.SetAttributeValue(attachmentContent, string.Empty);
attachment.SetAttributeValue(attachmentContentType, mimeType);
attachment.SetAttributeValue(attachmentFileName, fileName);
attachment.SetAttributeValue(attachmentName, "Attachment for " + storyOID.Momentless);
services.Save(attachment);
var attachmentOID = attachment.Oid;
Note the use of the MimeTypehelper object. The Resolve method of this object is used to determine the content type to use for the attachment asset. Also note that an empty string is passed for the Content attribute.
Once you've created the attachment asset and have its OID, you can then use the Attachments object to upload the binary data of the attachment. To use the Attachments object, you'll need to create a new V1Connector object, using the UseEndpoint method to set the attachment.img endpoint. You then use the System.IO.Stream class to read the binary data of the attachment, and then methods of the attachment class to write the stream.
V1Connector attachmentConnector = V1Connector
.WithInstanceUrl("<Server Base URI>")
.WithUserAgentHeader("AppName", "1.0")
.WithAccessToken("1.rWM8lKLk+PnyFxkEWVX5Kl2u6Jk=")
.UseEndpoint("attachment.img")
.Build();
var attachments = new Attachments(attachmentConnector);
string filePath = @"C:\Temp\";
using (Stream input = new FileStream(filePath + fileName, FileMode.Open, FileAccess.Read))
{
using (Stream output = attachments.GetWriteStream(attachmentOID.Key.ToString()))
{
byte[] buffer = new byte[input.Length + 1];
while (true)
{
int read = input.Read(buffer, 0, buffer.Length);
if (read <= 0)
break;
output.Write(buffer, 0, read);
}
}
}
attachments.SetWriteStream(attachmentOID.Key.ToString(), mimeType);
}
Note that as of the 15.1.0.0 release of the SDK, both the UseEndpoint method and the Attachmentsobject have been marked for deprecation.
Querying an Attachment
Prior to the 15.1.0.0 release of the SDK, querying an attachment for an asset involved using the GetReadStream method of the Attachments object to download the attachment data. Starting with the 15.1.0.0 release, a much simpler GetAttachmentmethod is available on the Services object.With the GetAttachment method, you no longer have to create a special V1Connector to the attachment.img endpoint to download attachments from Agility.
Querying an Attachment Using the Services Object
Getting an attachment from Agility involves getting the OID of the attachment, then using the GetAttachmentmethod of the Services object. In this example, a query is used to get all the attachments associated with Story ID 6052, and then write each attachment as a file:
Oid assetOID = services.GetOid("Story:6052");
//Get the attachment OID.
IAssetType assetType = services.Meta.GetAssetType("Attachment");
var query = new Query(assetType);
IAttributeDefinition filenameAttribute = assetType.GetAttributeDefinition("Filename");
IAttributeDefinition assetAttribute = assetType.GetAttributeDefinition("Asset");
query.Selection.Add(filenameAttribute);
query.Selection.Add(assetAttribute);
FilterTerm term = new FilterTerm(assetAttribute);
term.Equal(assetOID.Momentless);
query.Filter = term;
QueryResult result = services.Retrieve(query);
//Get the attachment data and save it to a file.
foreach (Asset attachment in result.Assets)
{
string fileName = attachment.GetAttribute(filenameAttribute).Value.ToString();
using (var fileStream = File.Create(fileName))
{
using (Stream blob = services.GetAttachment(attachment.Oid))
{
blob.CopyTo(fileStream);
}
}
Console.WriteLine("Saving file {0} with ID: {1}", fileName, attachment.Oid.Key.ToString());
}
Querying an Attachment Using the Attachment Object
Getting an attachment from Agility involves getting the OID of the attachment, then using the GetReadStream method of the Attachments object. In this example, a query is used to get all the attachments associated with Story ID 6052, and then write the attachments as files to the C:\Temp directory:
Oid assetOID = services.GetOid("Story:6052");
V1Connector attachmentConnector = V1Connector
.WithInstanceUrl("<Server Base URI>")
.WithUserAgentHeader("AppName", "1.0")
.WithAccessToken("1.rWM8lKLk+PnyFxkEWVX5Kl2u6Jk=")
.UseEndpoint("attachment.img")
.Build();
var attachments = new Attachments(attachmentConnector);
IAssetType assetType = services.Meta.GetAssetType("Attachment");
var query = new Query(assetType);
IAttributeDefinition filenameAttribute = assetType.GetAttributeDefinition("Filename");
IAttributeDefinition assetAttribute = assetType.GetAttributeDefinition("Asset");
query.Selection.Add(filenameAttribute);
query.Selection.Add(assetAttribute);
FilterTerm term = new FilterTerm(assetAttribute);
term.Equal(assetOID.Momentless);
query.Filter = term;
QueryResult result = services.Retrieve(query);
string filePath = @"C:\Temp\";
foreach (Asset attachment in result.Assets)
{
string fileName = attachment.GetAttribute(filenameAttribute).Value.ToString();
string attachmentID = attachment.Oid.Key.ToString();
using (var fileStream = File.Create(filePath + "v1_" + fileName))
{
using (Stream blob = attachments.GetReadStream(attachmentID))
{
blob.CopyTo(fileStream);
}
}
}
Note that as of the 15.1.0.0 release of the SDK, both the UseEndpoint method and the Attachmentsobject have been marked for deprecation.
Deleting an Attachment
Deleting an attachment is the same process as used for any other Agility asset, you use theGetOperation method to get the operation to execute, then call the ExecuteOperation method of the Services object, passing in the operation and the OID of the asset:
IOperation deleteOperation = services.Meta.GetOperation("Attachment.Delete");
services.ExecuteOperation(deleteOperation, services.GetOid("Attachment:6640"));
Adding an Embedded Image
Prior to the 15.1.0.0 release of the SDK, creating an embedded image for an asset involved first creating an EmbeddedImage asset then using the Attachments object to upload the embedded image data. Starting with the 15.1.0.0 release, a much simpler SaveEmbeddedImagemethod is available on the Services object.With the SaveEmbeddedImagemethod, you no longer have to create a special V1Connector to the embedded.img endpoint to upload embedded images to Agility.
Adding an Embedded Image Using the Services Object
Adding an embedded image using the Services object involves calling the SaveEmbeddedImage method and specifying the path and name to the image to embed, and the asset that it should be associated with.
In this example, a new story is created, an embedded image is associated with it, then the story's description field is modified to to include the embedded image:
//Create a new story.
var storyType = services.Meta.GetAssetType("Story");
var newStory = services.New(storyType, services.GetOid("Scope:0"));
var nameAttribute = storyType.GetAttributeDefinition("Name");
var descriptionAttribute = storyType.GetAttributeDefinition("Description");
var name = string.Format("Story with an embedded image");
newStory.SetAttributeValue(nameAttribute, name);
services.Save(newStory);
Oid storyOID = newStory.Oid;
//Create an embedded image.
string file = @"C:\Temp\versionone.jpg";
Oid embeddedImageOid = services.SaveEmbeddedImage(file, newStory);
var embeddedImageTag = string.Format("<p>Here's an embedded image:</p></br><img src=\"{0}\" alt=\"\" data-oid=\"{1}\" />", "embedded.img/" + embeddedImageOid.Key, embeddedImageOid.Momentless);
newStory.SetAttributeValue(descriptionAttribute, embeddedImageTag);
services.Save(newStory);
Adding an Embedded Image Using the Attachments Object
An embedded image is an image that you add to the Description attribute of an asset, and adding an embedded image follows a similar process as that of an attachment, except that it makes use of the embedded.img endpoint, and involves using the Embedded Image asset and adding a bit of HTML.
In the following example, a story is created in Scope:0 and an image file is read from the C:\Temp directory and added as an embedded image to the description of the story:
//Create a new story.
var storyType = services.Meta.GetAssetType("Story");
var newStory = services.New(storyType, services.GetOid("Scope:0"));
var nameAttribute = storyType.GetAttributeDefinition("Name");
var descriptionAttribute = storyType.GetAttributeDefinition("Description");
var name = string.Format("Story with an embedded image");
newStory.SetAttributeValue(nameAttribute, name);
services.Save(newStory);
Oid storyOID = newStory.Oid;
//Create an EmbeddedImage asset.
string fileName = "versionone.png";
string mimeType = MimeType.Resolve(fileName);
var embeddedImageType = services.Meta.GetAssetType("EmbeddedImage");
var newEmbeddedImage = services.New(embeddedImageType, Oid.Null);
var assetAttribute = embeddedImageType.GetAttributeDefinition("Asset");
var contentAttribute = embeddedImageType.GetAttributeDefinition("Content");
var contentTypeAttribute = embeddedImageType.GetAttributeDefinition("ContentType");
newEmbeddedImage.SetAttributeValue(assetAttribute, newStory.Oid);
newEmbeddedImage.SetAttributeValue(contentTypeAttribute, mimeType);
newEmbeddedImage.SetAttributeValue(contentAttribute, string.Empty);
services.Save(newEmbeddedImage);
var embeddedImageOID = newEmbeddedImage.Oid;
//Save the embdded image file data.
V1Connector attachmentConnector = V1Connector
.WithInstanceUrl("<Server Base URI>")
.WithUserAgentHeader("AppName", "1.0")
.WithAccessToken("1.rWM8lKLk+PnyFxkEWVX5Kl2u6Jk=")
.UseEndpoint("embedded.img")
.Build();
var attachments = new Attachments(attachmentConnector);
string key = embeddedImageOID.Key.ToString();
string filePath = @"C:\Temp\";
using (Stream input = new FileStream(filePath + fileName, FileMode.Open, FileAccess.Read))
{
using (Stream output = attachments.GetWriteStream(key))
{
byte[] buffer = new byte[input.Length + 1];
while (true)
{
int read = input.Read(buffer, 0, buffer.Length);
if (read <= 0)
break;
output.Write(buffer, 0, read);
}
}
}
attachments.SetWriteStream(key, mimeType);
//Add the embedded image to the story.
newStory.SetAttributeValue(descriptionAttribute,
string.Format("<p>Here's an embedded image:</p> </br> <img src=\"{0}\" alt=\"\" data-oid=\"{1}\" />", "embedded.img/" + key, embeddedImageOID.Momentless));
services.Save(newStory);
Note that as of the 15.1.0.0 release of the SDK, both the UseEndpoint method and the Attachmentsobject have been marked for deprecation.