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.Proxyto 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