Reputation: 419
I'm trying to write a tool (in C#) to read the embedded images from a VersionOne asset (story/defect) and store them locally (to be later able to migrate them to another place).
I found this link for reference: https://community.versionone.com/Digital.ai_Agility_Integrations/Developer_Library/Get_an_SDK/.NET_SDK/Working_with_Attachments_and_Images
With that I was able to read attachments successfully, but embedded images are still a problem.
My current attempt for this is like this:
public string StoreActiveV1ItemEmbeddedImages(PrimaryWorkitem v1Item, string targetPath, out int countStored)
{
string result = string.Empty;
countStored = 0;
if (v1Item == null) return "Error: No V1 item given to StoreActiveV1ItemEmbeddedImages!";
string filePath = targetPath;
if (!filePath.EndsWith("\\")) filePath += "\\";
if (!Directory.Exists(filePath)) return "Error: Given path '" + filePath + "' does not exist!";
IServices services = v1Instance.ApiClient.Services;
IMetaModel metaModel = v1Instance.ApiClient.MetaModel;
IAttachments attachments = v1Instance.ApiClient.Attachments;
Oid itemOid = services.GetOid(v1Item.ID.Token);
IAssetType assetType = metaModel.GetAssetType("EmbeddedImage");
var query = new Query(assetType);
IAttributeDefinition contentAttribute = assetType.GetAttributeDefinition("Content");
IAttributeDefinition contentTypeAttribute = assetType.GetAttributeDefinition("ContentType");
IAttributeDefinition assetAttribute = assetType.GetAttributeDefinition("Asset");
query.Selection.Add(contentAttribute);
query.Selection.Add(contentTypeAttribute);
query.Selection.Add(assetAttribute);
FilterTerm term = new FilterTerm(assetAttribute);
term.Equal(itemOid.Momentless);
query.Filter = term;
QueryResult queryResult = services.Retrieve(query);
foreach (Asset attachment in queryResult.Assets)
{
VersionOne.SDK.APIClient.Attribute attribute = attachment.GetAttribute(contentTypeAttribute);
string mimeType = "image/png";
if (attribute != null)
{
mimeType = attribute.Value.ToString();
}
string extension = MimeTypeStringToExtensionForImages(mimeType);
string fileName = (countStored + 1) + "_" + extension;
string attachmentID = attachment.Oid.Key.ToString();
string content = attachment.GetAttribute(contentAttribute).Value.ToString();
using (var fileStream = File.Create(filePath + "v1_img_" + fileName))
{
using (Stream blob = attachments.GetReadStream(content))
{
blob.CopyTo(fileStream);
}
countStored++;
}
}
return result;
}
I get a problem in this line
using (Stream blob = attachments.GetReadStream(content))
The server responds with a 404 like this: "The remote server returned an error: (404) Not Found.'"
Below can be seen the values in the debugger:
I've tried this with both the content (/VersionOne/embedded.img/2041732) and the attachmentID (2041732), but both result in the same error.
What should I use in this to find the data, or have I understood this more severely wrong and need to have a different approach?
Any help would be greatly appreciated!
Upvotes: 0
Views: 111
Reputation: 419
I think I found an answer. Not sure if it's the best or even a good answer, but at least an answer that for now seems to be enough for me.
The "content" is a path which can be appended to the VersionOne base URL and together they point to a location from which the embedded image data can be downloaded.
So my modified code now looks like this:
public string StoreActiveV1ItemEmbeddedImages(PrimaryWorkitem v1Item, string targetPath, out int countStored)
{
string result = string.Empty;
countStored = 0;
if (v1Item == null) return "Error: No V1 item given to StoreActiveV1ItemEmbeddedImages!";
string filePath = targetPath;
if (!filePath.EndsWith("\\")) filePath += "\\";
if (!Directory.Exists(filePath)) return "Error: Given path '" + filePath + "' does not exist!";
IServices services = v1Instance.ApiClient.Services;
IMetaModel metaModel = v1Instance.ApiClient.MetaModel;
//IAttachments attachments = v1Instance.ApiClient.Attachments;
Oid itemOid = services.GetOid(v1Item.ID.Token);
IAssetType assetType = metaModel.GetAssetType("EmbeddedImage");
var query = new Query(assetType);
IAttributeDefinition contentAttribute = assetType.GetAttributeDefinition("Content");
IAttributeDefinition contentTypeAttribute = assetType.GetAttributeDefinition("ContentType");
IAttributeDefinition assetAttribute = assetType.GetAttributeDefinition("Asset");
query.Selection.Add(contentAttribute);
query.Selection.Add(contentTypeAttribute);
query.Selection.Add(assetAttribute);
FilterTerm term = new FilterTerm(assetAttribute);
term.Equal(itemOid.Momentless);
query.Filter = term;
QueryResult queryResult = services.Retrieve(query);
foreach (Asset attachment in queryResult.Assets)
{
VersionOne.SDK.APIClient.Attribute attribute = attachment.GetAttribute(contentTypeAttribute);
string mimeType = "image/png";
if (attribute != null)
{
mimeType = attribute.Value.ToString();
}
string extension = MimeTypeStringToExtensionForImages(mimeType);
string attachmentID = attachment.Oid.Key.ToString();
string fileName = attachmentID + "_" + extension;
string content = attachment.GetAttribute(contentAttribute).Value.ToString();
string urlPath = Config.url;
const string commonBit = "/VersionOne";
if (urlPath.EndsWith(commonBit) && content.StartsWith(commonBit))
{
content = content.Replace(commonBit, string.Empty);
}
content = urlPath + content;
string finalFileName = filePath + "v1_img_" + fileName;
using (var client = new System.Net.WebClient())
{
client.Credentials = new System.Net.NetworkCredential(config.login, config.password);
client.DownloadFile(content, finalFileName);
countStored++;
}
}
return result;
}
So the URL is made from configuration data and the content string (just a bit of handling to ensure the "VersionOne" is only once in the final string) and then downloaded from there using WebClient instead of VersionOne API.
Upvotes: 0