I'm trying to programmatically download 90+ attachments from a user story in VSTS using TeamFoundation/VisualStudio C# API that I downloaded from Nuget. I was trying to use this example: https://intellitect.com/downloading-attachments-from-tfs/
However, that code seems to be out of date. I cannot seem to find these exact packages mentioned in article: nuget-bot.Microsoft.TeamFoundation.Client nuget-bot.Microsoft.TeamFoundation.WorkItemTracking.Client
However, I downloaded TFS packages that are there such as Microsoft.TeamFoundationServer.Client and Microsoft.TeamFoundationServer.ExtendedClient but the WorkItem class does not seem to have an Attachments no more. Does somebody know where I can find the Attachments property? I browsed the Object Browser in Visual Studio and cannot find it. Or can you suggest an alternate solution to get attachments from a work item? Thanks.
WebClient cannot authenticate to VSTS correctly. Instead of using WebClient to download the file, you can use
WorkItemServer.DownloadFile()
method inMicrosoft.TeamFoundation.WorkItemTracking.Proxy
to download the file. See this thread for details.
You can use below sample to download the attachment for a specific work item:
Note: Install the the nuget package Microsoft.TeamFoundationServer.ExtendedClient
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Proxy;
using System;
using System.IO;
namespace VSTS_DownloadWITAttachment
{
class Program
{
static void Main(string[] args)
{
TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(new Uri("https://account.visualstudio.com/"));
ttpc.EnsureAuthenticated();
WorkItemStore wistore = ttpc.GetService<WorkItemStore>();
WorkItem wi = wistore.GetWorkItem(94);
WorkItemServer wiserver = ttpc.GetService<WorkItemServer>();
string tmppath = wiserver.DownloadFile(wi.Attachments[0].Id); //Change the number to download other attachments if there are more then one attachments for the specific work item. e.g: [1] to download the second one.
string filename = string.Format("D:\\WITAttachments\\{0}-{1}", wi.Fields["ID"].Value, wi.Attachments[0].Name);
File.Copy(tmppath, filename);
}
}
}
Then you can try to query the work items and download the attachments in a loop for each of them. See Fetch work items with queries programatically in VSTS for details.
Below sample for your reference:
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Proxy;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
namespace DownloadWITAttachments
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("https://account.visualstudio.com");
string PAT = "xxxxxxxxxxxx";
string project = "ProjectName";
VssBasicCredential credentials = new VssBasicCredential("", PAT);
//create a wiql object and build our query
Wiql wiql = new Wiql()
{
Query = "Select * " +
"From WorkItems " +
"Where [Work Item Type] = 'User Story' " +
"And [System.TeamProject] = '" + project + "' " +
"And [System.State] <> 'Closed' " +
"And [System.AttachedFileCount] > 0 " +
"Order By [State] Asc, [Changed Date] Desc"
};
//create instance of work item tracking http client
using (WorkItemTrackingHttpClient workItemTrackingHttpClient = new WorkItemTrackingHttpClient(uri, credentials))
{
//execute the query to get the list of work items in the results
WorkItemQueryResult workItemQueryResult = workItemTrackingHttpClient.QueryByWiqlAsync(wiql).Result;
if (workItemQueryResult.WorkItems.Count() != 0)
{
//Download the first attachment for each work item.
foreach (var item in workItemQueryResult.WorkItems)
{
TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(uri);
ttpc.EnsureAuthenticated();
WorkItemStore wistore = ttpc.GetService<WorkItemStore>();
Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem wi = wistore.GetWorkItem(item.Id);
WorkItemServer wiserver = ttpc.GetService<WorkItemServer>();
string tmppath = wiserver.DownloadFile(wi.Attachments[0].Id);
string filename = string.Format("D:\\temp\\vsts\\{0}-{1}", wi.Fields["ID"].Value, wi.Attachments[0].Name);
File.Copy(tmppath, filename);
}
}
}
}
}
}
If you don't already have an access token for interacting with the API, generate one.
Make an API call to get the expanded work item information. Note the $expand=all
parameter to retrieve all item details.
GET https://{account}.visualstudio.com/{project}/_apis/wit/workitems/115258?api-version=4.1&$expand=all
The response should look something like this (if the item has attachments).
{
"id": 115258,
"rev": 4,
"fields": {
"System.Id": 115258,
"System.AreaId": 2643
...and so on...
},
"relations": [
{
"rel": "AttachedFile",
"url": "https://{account}.visualstudio.com/d6c4b828-0f7e-4b69-a356-a92c0ec3cd07/_apis/wit/attachments/5682f031-4b09-478c-8042-0d2a998905e4",
"attributes": {
"authorizedDate": "2018-04-30T19:34:09.763Z",
"id": 2015371,
"resourceCreatedDate": "2018-04-30T19:34:07.873Z",
"resourceModifiedDate": "2018-04-30T19:32:16.057Z",
"revisedDate": "9999-01-01T00:00:00Z",
"resourceSize": 47104,
"name": "file.jpg"
}
}
]
}
Iterate over the relations
where the rel
is AttachedFile
and call the url
to get the attachment content.
GET https://{account}.visualstudio.com/d6c4b828-0f7e-4b69-a356-a92c0ec3cd07/_apis/wit/attachments/5682f031-4b09-478c-8042-0d2a998905e4
Sources:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With